This commit is contained in:
2026-09-29 22:02:03 +03:30
parent 7ee78b070a
commit 5a2c4cabfd
4 changed files with 29 additions and 25 deletions
+10
View File
@@ -3,6 +3,7 @@ import { NotificationTone, OrderStatus } from '@prisma/client';
/** رویدادهایی که برای مشتری اعلان و پیامک تولید می‌کنند. */
export type CustomerEvent =
| 'order_submitted'
| 'order_registered_by_admin'
| 'invoice_priced'
| 'checks_approved'
| 'checks_need_fix'
@@ -87,6 +88,15 @@ export const CUSTOMER_EVENTS: Record<CustomerEvent, CustomerEventTemplate> = {
tone: NotificationTone.info,
smsSwitch: 'smsOrderRegistered',
},
order_registered_by_admin: {
title: 'پیش‌سفارش برای شما ثبت شد',
body: (c) =>
`پیش‌سفارش ${c.orderNumber} توسط کارشناسان ما ثبت شد و برای قیمت‌گذاری در صف بررسی است.`,
sms: (c) =>
`پیش‌سفارش ${c.orderNumber} در ${c.appName} برای شما ثبت شد. پس از قیمت‌گذاری مطلع می‌شوید.`,
tone: NotificationTone.info,
smsSwitch: 'smsOrderRegistered',
},
invoice_priced: {
title: 'فاکتور سفارش شما صادر شد',
body: (c) =>
-11
View File
@@ -1,7 +1,6 @@
import {
ArrayMinSize,
IsArray,
IsBoolean,
IsDateString,
IsEnum,
IsNumber,
@@ -262,16 +261,6 @@ export class UpdateOrderStatusDto {
@IsOptional()
@IsString()
note?: string;
@IsOptional()
@IsString()
reason?: string;
/** عبور از ماشین وضعیت؛ فقط با ذکر دلیل و با ثبت در تاریخچه. */
@IsOptional()
@Type(() => Boolean)
@IsBoolean()
force?: boolean;
}
export class UpdateOrderChecksDto {
+9 -14
View File
@@ -327,6 +327,11 @@ export class OrdersService {
}),
);
this.notifications.dispatchOrderEvent(
order.id,
'order_registered_by_admin',
);
return this.mapOrder(order, true);
}
@@ -511,15 +516,7 @@ export class OrdersService {
return this.mapOrder(order, true);
}
if (dto.force) {
if (!dto.reason?.trim()) {
throw new BadRequestException(
'برای تغییر دستی وضعیت، ذکر دلیل الزامی است',
);
}
} else {
assertTransition(order.status, dto.status);
}
assertTransition(order.status, dto.status);
// فاکتور بدون مبلغ نباید برای مشتری ارسال شود
if (
@@ -550,10 +547,8 @@ export class OrdersService {
fromStatus: order.status,
toStatus: dto.status,
changedBy: `admin:${adminId}`,
note: dto.force
? `تغییر دستی وضعیت${dto.note ? ` — ${dto.note}` : ''}`
: dto.note || null,
reason: dto.reason || null,
note: dto.note || null,
reason: null,
},
});
@@ -568,7 +563,7 @@ export class OrdersService {
const event = eventForStatus(dto.status);
if (event) {
this.notifications.dispatchOrderEvent(id, event, {
note: dto.reason ?? dto.note ?? null,
note: dto.note ?? null,
});
}
+10
View File
@@ -17,6 +17,8 @@ const PRODUCT_INCLUDE = {
isNew: true,
colorsJson: true,
imageUrlsJson: true,
widthCm: true,
composition: true,
catalog: { select: { name: true, code: true } },
},
},
@@ -143,11 +145,15 @@ export class SuggestionsService {
isNew: boolean;
colorsJson: unknown;
imageUrlsJson: unknown;
widthCm: number | null;
composition: string | null;
catalog: { name: string; code: string } | null;
};
}) {
const colorEntries = parseColorEntries(r.product.colorsJson, r.product.color);
const images = mergeProductImages(colorEntries, r.product.imageUrlsJson);
const colors = colorEntries.map((entry) => entry.name);
const colorImages = colorEntries.map((entry) => entry.imageUrl ?? null);
const catalogName = r.product.catalog?.name;
const subtitle =
r.product.subtitle?.trim() ||
@@ -167,6 +173,10 @@ export class SuggestionsService {
color: r.product.color,
subtitle,
imageUrl: images[0] ?? undefined,
colors,
colorImages,
widthCm: r.product.widthCm ?? undefined,
composition: r.product.composition?.trim() || undefined,
isNew: r.product.isNew,
note: r.note ?? undefined,
isActive: r.isActive,