mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 22:58:17 +00:00
feat: add toggle to DJF
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { ManualPaymentSettingsService } from "./manual-payment-settings.service";
|
||||
import type { ManualPaymentSetting } from "./entities/manual-payment-setting.entity";
|
||||
|
||||
/** Single-row repository stub: enough for get/update, nothing more. */
|
||||
const repoWith = (row: Partial<ManualPaymentSetting>) => {
|
||||
const stored = { id: "settings-1", ...row } as ManualPaymentSetting;
|
||||
return {
|
||||
findOne: async () => stored,
|
||||
create: (v: Partial<ManualPaymentSetting>) => v as ManualPaymentSetting,
|
||||
save: async (v: ManualPaymentSetting) => v,
|
||||
update: async (_id: string, patch: Partial<ManualPaymentSetting>) => {
|
||||
Object.assign(stored, patch);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const serviceWith = (row: Partial<ManualPaymentSetting>) =>
|
||||
new ManualPaymentSettingsService(repoWith(row) as never);
|
||||
|
||||
describe("DJF currency switch", () => {
|
||||
it("offers DJF, and accepts it, while the switch is on", async () => {
|
||||
const service = serviceWith({ djfPaymentsEnabled: true });
|
||||
|
||||
await expect(service.offeredCurrencies()).resolves.toEqual([
|
||||
"ETB",
|
||||
"USD",
|
||||
"DJF",
|
||||
]);
|
||||
await expect(service.isCurrencyOffered("DJF")).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it("drops DJF from the offered currencies once switched off", async () => {
|
||||
const service = serviceWith({ djfPaymentsEnabled: false });
|
||||
|
||||
await expect(service.offeredCurrencies()).resolves.toEqual(["ETB", "USD"]);
|
||||
await expect(service.isCurrencyOffered("djf")).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it("never switches off ETB or USD — only DJF has a currency-level switch", async () => {
|
||||
const service = serviceWith({ djfPaymentsEnabled: false });
|
||||
|
||||
await expect(service.isCurrencyOffered("ETB")).resolves.toBe(true);
|
||||
await expect(service.isCurrencyOffered("USD")).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it("leaves the manual rail alone when the currency switch flips", async () => {
|
||||
const service = serviceWith({ djfEnabled: true, djfPaymentsEnabled: true });
|
||||
|
||||
const updated = await service.update({ djfPaymentsEnabled: false }, "user-1");
|
||||
|
||||
expect(updated.djfPaymentsEnabled).toBe(false);
|
||||
// Existing DJF invoices stay hand-settleable, so nothing is stranded.
|
||||
expect(updated.djfEnabled).toBe(true);
|
||||
await expect(service.isEnabled("DJF")).resolves.toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -20,4 +20,12 @@ export class UpdateManualPaymentSettingDto {
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
djfEnabled?: boolean;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
"Accept DJF as a payment currency at all — booking forms and online payment",
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
djfPaymentsEnabled?: boolean;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,17 @@ export class ManualPaymentSetting extends BaseEntity {
|
||||
@Column({ name: "djf_enabled", type: "boolean", default: true })
|
||||
djfEnabled!: boolean;
|
||||
|
||||
/**
|
||||
* Whether DJF may be used as a payment currency AT ALL — offered on the
|
||||
* booking/shipment forms and accepted for online payment (Waafi / CAC Bank).
|
||||
*
|
||||
* Wider than `djfEnabled`, which only governs the manual rail. Off leaves
|
||||
* existing DJF invoices settleable by hand (while `djfEnabled` is on), so
|
||||
* switching it off strands nothing — it only stops new DJF business.
|
||||
*/
|
||||
@Column({ name: "djf_payments_enabled", type: "boolean", default: true })
|
||||
djfPaymentsEnabled!: boolean;
|
||||
|
||||
/** IAM user id of the last operator to change either toggle. */
|
||||
@Column({ name: "updated_by_id", type: "uuid", nullable: true })
|
||||
updatedById?: string | null;
|
||||
|
||||
@@ -30,13 +30,26 @@ export class ManualPaymentSettingsController {
|
||||
return this.service.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Which currencies may be picked for new bookings and paid online. Read by
|
||||
* the customer portal's booking forms, so it stays open like the other
|
||||
* form-shaping settings reads (file-upload / dropdown settings) — it exposes
|
||||
* nothing beyond what the currency picker already shows.
|
||||
*/
|
||||
@Get("currencies")
|
||||
@ApiOperation({ summary: "Currencies customers may be billed and pay in" })
|
||||
currencies() {
|
||||
return this.service.offeredCurrencies();
|
||||
}
|
||||
|
||||
@Patch()
|
||||
@BookingStaff([
|
||||
FREIGHT_PERMS.settings.manualPayment.manage,
|
||||
FREIGHT_PERMS.admin,
|
||||
])
|
||||
@ApiOperation({
|
||||
summary: "Enable or disable manual invoice settlement for ETB and/or USD",
|
||||
summary:
|
||||
"Enable or disable manual invoice settlement per currency, and whether DJF is accepted at all",
|
||||
})
|
||||
update(
|
||||
@Body() dto: UpdateManualPaymentSettingDto,
|
||||
|
||||
@@ -37,7 +37,12 @@ export class ManualPaymentSettingsService {
|
||||
if (existing) return existing;
|
||||
|
||||
return this.repository.save(
|
||||
this.repository.create({ etbEnabled: false, usdEnabled: true, djfEnabled: true }),
|
||||
this.repository.create({
|
||||
etbEnabled: false,
|
||||
usdEnabled: true,
|
||||
djfEnabled: true,
|
||||
djfPaymentsEnabled: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,9 +65,34 @@ export class ManualPaymentSettingsService {
|
||||
return setting[field];
|
||||
}
|
||||
|
||||
/**
|
||||
* Currencies customers may be billed and pay in right now. ETB and USD are
|
||||
* always offered; DJF only while its currency-level switch is on. Read by
|
||||
* the booking forms (portal and backoffice) to decide which options to show.
|
||||
*/
|
||||
async offeredCurrencies(): Promise<ManualPaymentCurrency[]> {
|
||||
const setting = await this.get();
|
||||
return setting.djfPaymentsEnabled ? ["ETB", "USD", "DJF"] : ["ETB", "USD"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a currency may be used for NEW business and online payment — the
|
||||
* currency-level switch, not the manual rail's {@link isEnabled}. Only DJF
|
||||
* is switchable; ETB and USD have no off switch.
|
||||
*/
|
||||
async isCurrencyOffered(currency: string | null | undefined): Promise<boolean> {
|
||||
if (currency?.toUpperCase() !== "DJF") return true;
|
||||
return (await this.get()).djfPaymentsEnabled;
|
||||
}
|
||||
|
||||
/** Flip any toggle; an omitted field leaves that currency unchanged. */
|
||||
async update(
|
||||
patch: { etbEnabled?: boolean; usdEnabled?: boolean; djfEnabled?: boolean },
|
||||
patch: {
|
||||
etbEnabled?: boolean;
|
||||
usdEnabled?: boolean;
|
||||
djfEnabled?: boolean;
|
||||
djfPaymentsEnabled?: boolean;
|
||||
},
|
||||
updatedById?: string | null,
|
||||
): Promise<ManualPaymentSetting> {
|
||||
const current = await this.get();
|
||||
@@ -70,11 +100,14 @@ export class ManualPaymentSettingsService {
|
||||
...(patch.etbEnabled === undefined ? {} : { etbEnabled: patch.etbEnabled }),
|
||||
...(patch.usdEnabled === undefined ? {} : { usdEnabled: patch.usdEnabled }),
|
||||
...(patch.djfEnabled === undefined ? {} : { djfEnabled: patch.djfEnabled }),
|
||||
...(patch.djfPaymentsEnabled === undefined
|
||||
? {}
|
||||
: { djfPaymentsEnabled: patch.djfPaymentsEnabled }),
|
||||
updatedById: updatedById ?? null,
|
||||
});
|
||||
const updated = await this.get();
|
||||
this.logger.warn(
|
||||
`Manual payment channels set to ETB=${updated.etbEnabled} USD=${updated.usdEnabled} DJF=${updated.djfEnabled} by ${updatedById ?? "unknown user"}`,
|
||||
`Manual payment channels set to ETB=${updated.etbEnabled} USD=${updated.usdEnabled} DJF=${updated.djfEnabled} (DJF accepted as a currency: ${updated.djfPaymentsEnabled}) by ${updatedById ?? "unknown user"}`,
|
||||
);
|
||||
return updated;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user