feat(loyalty): organization loyalty settings API
Adds GET and PUT /marketing/loyalty-settings and GET /marketing/loyalty-settings/history (docs/prd-point-coin.md F2, PC-302). The settings are the point value, the exchange rate, transfer limits and the stored expiry settings. PUT merges the body like the outlet settings and is limited to loyalty managers. Every response carries the impact of the change on the balances in circulation: outstanding EnakPoint and EnakCoin, their rupiah value, and the coins exchanged into points, before and after. With ?dry_run=true nothing is saved and the response lists the keys that would change, for the warning shown before saving. Saving records each change in loyalty_setting_changes with who made it; history can be filtered to one outlet. Changing the value leaves what was already written alone. The diff behind saving and previewing is shared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8370851ed2
commit
fe2f459b03
@@ -141,27 +141,11 @@ func (p *LoyaltySettingsProcessor) UpdateOrganization(ctx context.Context, organ
|
||||
// on its default and not changed is not written, so the history only shows real
|
||||
// changes. current and next list the same keys in the same order.
|
||||
func (p *LoyaltySettingsProcessor) apply(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, changedBy uuid.UUID, raw map[string]string, current, next []loyaltyField, store func(key string, value *string) error) ([]models.LoyaltySettingChange, error) {
|
||||
var rows []entities.LoyaltySettingChange
|
||||
for i, field := range next {
|
||||
newValue := field.format()
|
||||
if equalOptionalStrings(current[i].format(), newValue) {
|
||||
continue
|
||||
}
|
||||
var oldValue *string
|
||||
if v, ok := raw[field.key]; ok {
|
||||
oldValue = &v
|
||||
}
|
||||
if err := store(field.key, newValue); err != nil {
|
||||
rows := diffLoyaltyFields(organizationID, outletID, changedBy, raw, current, next)
|
||||
for _, row := range rows {
|
||||
if err := store(row.Key, row.NewValue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows = append(rows, entities.LoyaltySettingChange{
|
||||
OrganizationID: organizationID,
|
||||
OutletID: outletID,
|
||||
Key: field.key,
|
||||
OldValue: oldValue,
|
||||
NewValue: newValue,
|
||||
ChangedBy: changedBy,
|
||||
})
|
||||
}
|
||||
if err := p.repo.InsertChanges(ctx, rows); err != nil {
|
||||
return nil, err
|
||||
@@ -410,3 +394,49 @@ func equalOptionalStrings(a, b *string) bool {
|
||||
}
|
||||
return *a == *b
|
||||
}
|
||||
|
||||
// diffLoyaltyFields lists the keys whose effective value differs between current and
|
||||
// next, with the stored old value (nil when the key was on its default). current and
|
||||
// next list the same keys in the same order.
|
||||
func diffLoyaltyFields(organizationID uuid.UUID, outletID *uuid.UUID, changedBy uuid.UUID, raw map[string]string, current, next []loyaltyField) []entities.LoyaltySettingChange {
|
||||
var rows []entities.LoyaltySettingChange
|
||||
for i, field := range next {
|
||||
newValue := field.format()
|
||||
if equalOptionalStrings(current[i].format(), newValue) {
|
||||
continue
|
||||
}
|
||||
var oldValue *string
|
||||
if v, ok := raw[field.key]; ok {
|
||||
oldValue = &v
|
||||
}
|
||||
rows = append(rows, entities.LoyaltySettingChange{
|
||||
OrganizationID: organizationID,
|
||||
OutletID: outletID,
|
||||
Key: field.key,
|
||||
OldValue: oldValue,
|
||||
NewValue: newValue,
|
||||
ChangedBy: changedBy,
|
||||
})
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
// PreviewOrganization validates new organization settings and lists what saving them
|
||||
// would change, without writing anything.
|
||||
func (p *LoyaltySettingsProcessor) PreviewOrganization(ctx context.Context, organizationID uuid.UUID, settings models.OrganizationLoyaltySettings) ([]models.LoyaltySettingChange, error) {
|
||||
if err := validateLoyaltyFields(organizationLoyaltyFields(&settings)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw, err := p.repo.GetOrganizationValues(ctx, organizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var current models.OrganizationLoyaltySettings
|
||||
loadLoyaltyFields(organizationLoyaltyFields(¤t), raw, "organization "+organizationID.String())
|
||||
rows := diffLoyaltyFields(organizationID, nil, uuid.Nil, raw, organizationLoyaltyFields(¤t), organizationLoyaltyFields(&settings))
|
||||
changes := make([]models.LoyaltySettingChange, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
changes = append(changes, loyaltySettingChangeModel(row))
|
||||
}
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user