From 816fa7a7fc00dc33f6a304015e722762a02ee9ab Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Wed, 1 Jul 2026 20:41:27 +0330 Subject: [PATCH] add sms quantity --- ...ation20260701180000_addRestaurantCredit.ts | 38 +++++++++++++++++++ .../notifications/events/sms.events.ts | 1 + .../interfaces/jobs-queue.interface.ts | 2 + .../notifications/listeners/sms.listeners.ts | 7 ++-- .../processors/direct-sms.processor.ts | 7 +++- .../restaurant-credit-transaction.entity.ts | 26 +++++++++++++ .../restaurants/entities/restaurant.entity.ts | 3 ++ ...restaurant-credit-transaction.interface.ts | 10 +++++ src/modules/restaurants/restaurants.module.ts | 3 +- 9 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 database/migrations/Migration20260701180000_addRestaurantCredit.ts create mode 100644 src/modules/restaurants/entities/restaurant-credit-transaction.entity.ts create mode 100644 src/modules/restaurants/interface/restaurant-credit-transaction.interface.ts diff --git a/database/migrations/Migration20260701180000_addRestaurantCredit.ts b/database/migrations/Migration20260701180000_addRestaurantCredit.ts new file mode 100644 index 0000000..8087533 --- /dev/null +++ b/database/migrations/Migration20260701180000_addRestaurantCredit.ts @@ -0,0 +1,38 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20260701180000_addRestaurantCredit extends Migration { + + override async up(): Promise { + this.addSql(`alter table "restaurants" add column "credit" numeric(10,0) not null default 0;`); + + this.addSql(` + create table "restaurant_credit_transactions" ( + "id" char(26) not null, + "created_at" timestamptz not null default now(), + "updated_at" timestamptz not null default now(), + "deleted_at" timestamptz null, + "restaurant_id" char(26) not null, + "amount" numeric(10,0) not null, + "balance" numeric(10,0) not null, + "type" text check ("type" in ('credit', 'debit')) not null, + "reason" text check ("reason" in ('sms_send', 'deposit', 'adjustment')) not null, + constraint "restaurant_credit_transactions_pkey" primary key ("id") + ); + `); + this.addSql(`create index "restaurant_credit_transactions_created_at_index" on "restaurant_credit_transactions" ("created_at");`); + this.addSql(`create index "restaurant_credit_transactions_deleted_at_index" on "restaurant_credit_transactions" ("deleted_at");`); + this.addSql(`create index "restaurant_credit_transactions_restaurant_id_index" on "restaurant_credit_transactions" ("restaurant_id");`); + this.addSql(` + alter table "restaurant_credit_transactions" + add constraint "restaurant_credit_transactions_restaurant_id_foreign" + foreign key ("restaurant_id") references "restaurants" ("id") on update cascade; + `); + } + + override async down(): Promise { + this.addSql(`alter table "restaurant_credit_transactions" drop constraint if exists "restaurant_credit_transactions_restaurant_id_foreign";`); + this.addSql(`drop table if exists "restaurant_credit_transactions" cascade;`); + this.addSql(`alter table "restaurants" drop column if exists "credit";`); + } + +} diff --git a/src/modules/notifications/events/sms.events.ts b/src/modules/notifications/events/sms.events.ts index 303b366..86a3644 100644 --- a/src/modules/notifications/events/sms.events.ts +++ b/src/modules/notifications/events/sms.events.ts @@ -2,6 +2,7 @@ export class SmsSentEvent { constructor( public readonly phoneNumber: string, public readonly restaurantId: string, + public readonly quantity: number = 1, ) {} } diff --git a/src/modules/notifications/interfaces/jobs-queue.interface.ts b/src/modules/notifications/interfaces/jobs-queue.interface.ts index d468465..8974b45 100644 --- a/src/modules/notifications/interfaces/jobs-queue.interface.ts +++ b/src/modules/notifications/interfaces/jobs-queue.interface.ts @@ -12,6 +12,8 @@ export interface DirectSmsQueueJob { templateId: string; params?: Record; restaurantId?: string; + /** Number of SMS units billed (1 for short templates, 3–4 for long ones). Defaults to 1. */ + quantity?: number; } export interface PushNotificationQueueJob { title: string; diff --git a/src/modules/notifications/listeners/sms.listeners.ts b/src/modules/notifications/listeners/sms.listeners.ts index 50534e5..494b4ce 100644 --- a/src/modules/notifications/listeners/sms.listeners.ts +++ b/src/modules/notifications/listeners/sms.listeners.ts @@ -33,15 +33,16 @@ export class SmsListeners { return; } + const quantity = event.quantity ?? 1; + let smsLog = await this.em.findOne(SmsLog, { restaurant: event.restaurantId }); if (smsLog) { - smsLog.count += 1; + smsLog.count += quantity; } else { smsLog = this.em.create(SmsLog, { restaurant, - count: 1, - createdAt: new Date(), + count: quantity, }); } diff --git a/src/modules/notifications/processors/direct-sms.processor.ts b/src/modules/notifications/processors/direct-sms.processor.ts index 22d035e..1dd326f 100644 --- a/src/modules/notifications/processors/direct-sms.processor.ts +++ b/src/modules/notifications/processors/direct-sms.processor.ts @@ -19,7 +19,7 @@ export class DirectSmsProcessor extends WorkerHost { } async process(job: Job) { - const { phone, templateId, params, restaurantId } = job.data; + const { phone, templateId, params, restaurantId, quantity = 1 } = job.data; this.logger.log(`Processing direct SMS - phone: ${phone}, templateId: ${templateId}`); @@ -33,7 +33,10 @@ export class DirectSmsProcessor extends WorkerHost { this.logger.log(`Direct SMS sent successfully to ${phone}`); if (restaurantId) { - this.eventEmitter.emit(SmsSentEvent.name, new SmsSentEvent(phone, restaurantId)); + this.eventEmitter.emit( + SmsSentEvent.name, + new SmsSentEvent(phone, restaurantId, quantity), + ); } return { success: true }; diff --git a/src/modules/restaurants/entities/restaurant-credit-transaction.entity.ts b/src/modules/restaurants/entities/restaurant-credit-transaction.entity.ts new file mode 100644 index 0000000..51d2abe --- /dev/null +++ b/src/modules/restaurants/entities/restaurant-credit-transaction.entity.ts @@ -0,0 +1,26 @@ +import { Entity, Enum, Index, ManyToOne, Property } from '@mikro-orm/core'; +import { BaseEntity } from '../../../common/entities/base.entity'; +import { Restaurant } from './restaurant.entity'; +import { + RestaurantCreditTransactionReason, + RestaurantCreditTransactionType, +} from '../interface/restaurant-credit-transaction.interface'; + +@Entity({ tableName: 'restaurant_credit_transactions' }) +@Index({ properties: ['restaurant'] }) +export class RestaurantCreditTransaction extends BaseEntity { + @ManyToOne(() => Restaurant) + restaurant!: Restaurant; + + @Property({ type: 'decimal', precision: 10, scale: 0 }) + amount!: number; + + @Property({ type: 'decimal', precision: 10, scale: 0 }) + balance!: number; + + @Enum(() => RestaurantCreditTransactionType) + type!: RestaurantCreditTransactionType; + + @Enum(() => RestaurantCreditTransactionReason) + reason!: RestaurantCreditTransactionReason; +} diff --git a/src/modules/restaurants/entities/restaurant.entity.ts b/src/modules/restaurants/entities/restaurant.entity.ts index 1ca8e7d..7d016ae 100644 --- a/src/modules/restaurants/entities/restaurant.entity.ts +++ b/src/modules/restaurants/entities/restaurant.entity.ts @@ -77,6 +77,9 @@ export class Restaurant extends BaseEntity { @Property({ type: 'decimal', default: 0 }) vat?: number = 0; + @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 }) + credit?: number = 0; + @Property() domain!: string; diff --git a/src/modules/restaurants/interface/restaurant-credit-transaction.interface.ts b/src/modules/restaurants/interface/restaurant-credit-transaction.interface.ts new file mode 100644 index 0000000..f285071 --- /dev/null +++ b/src/modules/restaurants/interface/restaurant-credit-transaction.interface.ts @@ -0,0 +1,10 @@ +export enum RestaurantCreditTransactionType { + CREDIT = 'credit', + DEBIT = 'debit', +} + +export enum RestaurantCreditTransactionReason { + SMS_SEND = 'sms_send', + DEPOSIT = 'deposit', + ADJUSTMENT = 'adjustment', +} diff --git a/src/modules/restaurants/restaurants.module.ts b/src/modules/restaurants/restaurants.module.ts index 8a35be2..d3e6894 100644 --- a/src/modules/restaurants/restaurants.module.ts +++ b/src/modules/restaurants/restaurants.module.ts @@ -14,11 +14,12 @@ import { AuthModule } from '../auth/auth.module'; import { UtilsModule } from '../utils/utils.module'; import { Background } from './entities/background.entity'; import { BackgroundRepository } from './repositories/background.repository'; +import { RestaurantCreditTransaction } from './entities/restaurant-credit-transaction.entity'; @Module({ controllers: [RestaurantsController, ScheduleController], providers: [RestaurantsService, RestRepository, ScheduleRepository, ScheduleService, RestaurantCrone, BackgroundRepository], - imports: [MikroOrmModule.forFeature([Restaurant, Schedule, Background]), + imports: [MikroOrmModule.forFeature([Restaurant, Schedule, Background, RestaurantCreditTransaction]), JwtModule, forwardRef(() => AuthModule), UtilsModule], exports: [RestRepository, ScheduleRepository, ScheduleService, RestaurantsService], })