Enable Montact CRM integration and admin CRM APIs.
This commit is contained in:
@@ -18,6 +18,10 @@ RUN_SEED=false
|
|||||||
|
|
||||||
JWT_SECRET=change-me-to-a-long-random-string
|
JWT_SECRET=change-me-to-a-long-random-string
|
||||||
JWT_EXPIRES_IN=7d
|
JWT_EXPIRES_IN=7d
|
||||||
|
|
||||||
|
# Montact CRM (اختیاری): توکن ثابت mk_live_...؛ اگر خالی باشد فقط JWT ادمین قبول میشود
|
||||||
|
# MONTACT_API_TOKEN=
|
||||||
|
# MONTACT_STORE_ID=1
|
||||||
MOCK_OTP_CODE=1234
|
MOCK_OTP_CODE=1234
|
||||||
OTP_TTL_MINUTES=5
|
OTP_TTL_MINUTES=5
|
||||||
|
|
||||||
|
|||||||
+592
-2
@@ -134757,6 +134757,7 @@ var require_crm_controller = __commonJS({
|
|||||||
Object.defineProperty(exports2, "__esModule", { value: true });
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
||||||
exports2.CrmController = void 0;
|
exports2.CrmController = void 0;
|
||||||
var common_12 = require_common();
|
var common_12 = require_common();
|
||||||
|
var swagger_12 = require_dist6();
|
||||||
var client_1 = require_default2();
|
var client_1 = require_default2();
|
||||||
var auth_decorators_1 = require_auth_decorators();
|
var auth_decorators_1 = require_auth_decorators();
|
||||||
var crm_service_1 = require_crm_service();
|
var crm_service_1 = require_crm_service();
|
||||||
@@ -134803,6 +134804,8 @@ var require_crm_controller = __commonJS({
|
|||||||
__metadata2("design:returntype", void 0)
|
__metadata2("design:returntype", void 0)
|
||||||
], CrmController.prototype, "updateOpportunity", null);
|
], CrmController.prototype, "updateOpportunity", null);
|
||||||
exports2.CrmController = CrmController = __decorate2([
|
exports2.CrmController = CrmController = __decorate2([
|
||||||
|
(0, swagger_12.ApiTags)("Crm"),
|
||||||
|
(0, swagger_12.ApiBearerAuth)(),
|
||||||
(0, common_12.Controller)("admin/crm"),
|
(0, common_12.Controller)("admin/crm"),
|
||||||
(0, auth_decorators_1.AuthType)("admin"),
|
(0, auth_decorators_1.AuthType)("admin"),
|
||||||
(0, auth_decorators_1.Roles)("admin", "sales"),
|
(0, auth_decorators_1.Roles)("admin", "sales"),
|
||||||
@@ -134832,6 +134835,7 @@ var require_customer_crm_controller = __commonJS({
|
|||||||
Object.defineProperty(exports2, "__esModule", { value: true });
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
||||||
exports2.AdminNextActionsController = exports2.CustomerCrmController = void 0;
|
exports2.AdminNextActionsController = exports2.CustomerCrmController = void 0;
|
||||||
var common_12 = require_common();
|
var common_12 = require_common();
|
||||||
|
var swagger_12 = require_dist6();
|
||||||
var auth_decorators_1 = require_auth_decorators();
|
var auth_decorators_1 = require_auth_decorators();
|
||||||
var current_user_decorator_1 = require_current_user_decorator();
|
var current_user_decorator_1 = require_current_user_decorator();
|
||||||
var crm_service_1 = require_crm_service();
|
var crm_service_1 = require_crm_service();
|
||||||
@@ -134912,6 +134916,8 @@ var require_customer_crm_controller = __commonJS({
|
|||||||
__metadata2("design:returntype", void 0)
|
__metadata2("design:returntype", void 0)
|
||||||
], CustomerCrmController.prototype, "setAssignment", null);
|
], CustomerCrmController.prototype, "setAssignment", null);
|
||||||
exports2.CustomerCrmController = CustomerCrmController = __decorate2([
|
exports2.CustomerCrmController = CustomerCrmController = __decorate2([
|
||||||
|
(0, swagger_12.ApiTags)("CustomerCrm"),
|
||||||
|
(0, swagger_12.ApiBearerAuth)(),
|
||||||
(0, common_12.Controller)("admin/customers"),
|
(0, common_12.Controller)("admin/customers"),
|
||||||
(0, auth_decorators_1.AuthType)("admin"),
|
(0, auth_decorators_1.AuthType)("admin"),
|
||||||
(0, auth_decorators_1.Roles)("admin", "sales"),
|
(0, auth_decorators_1.Roles)("admin", "sales"),
|
||||||
@@ -134939,6 +134945,8 @@ var require_customer_crm_controller = __commonJS({
|
|||||||
__metadata2("design:returntype", void 0)
|
__metadata2("design:returntype", void 0)
|
||||||
], AdminNextActionsController.prototype, "update", null);
|
], AdminNextActionsController.prototype, "update", null);
|
||||||
exports2.AdminNextActionsController = AdminNextActionsController = __decorate2([
|
exports2.AdminNextActionsController = AdminNextActionsController = __decorate2([
|
||||||
|
(0, swagger_12.ApiTags)("CustomerCrm"),
|
||||||
|
(0, swagger_12.ApiBearerAuth)(),
|
||||||
(0, common_12.Controller)("admin/next-actions"),
|
(0, common_12.Controller)("admin/next-actions"),
|
||||||
(0, auth_decorators_1.AuthType)("admin"),
|
(0, auth_decorators_1.AuthType)("admin"),
|
||||||
(0, auth_decorators_1.Roles)("admin", "sales"),
|
(0, auth_decorators_1.Roles)("admin", "sales"),
|
||||||
@@ -134968,6 +134976,7 @@ var require_admin_agents_controller = __commonJS({
|
|||||||
Object.defineProperty(exports2, "__esModule", { value: true });
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
||||||
exports2.AdminAssignmentRequestsController = exports2.AdminAgentsController = void 0;
|
exports2.AdminAssignmentRequestsController = exports2.AdminAgentsController = void 0;
|
||||||
var common_12 = require_common();
|
var common_12 = require_common();
|
||||||
|
var swagger_12 = require_dist6();
|
||||||
var client_1 = require_default2();
|
var client_1 = require_default2();
|
||||||
var auth_decorators_1 = require_auth_decorators();
|
var auth_decorators_1 = require_auth_decorators();
|
||||||
var current_user_decorator_1 = require_current_user_decorator();
|
var current_user_decorator_1 = require_current_user_decorator();
|
||||||
@@ -135025,6 +135034,8 @@ var require_admin_agents_controller = __commonJS({
|
|||||||
__metadata2("design:returntype", void 0)
|
__metadata2("design:returntype", void 0)
|
||||||
], AdminAgentsController.prototype, "customers", null);
|
], AdminAgentsController.prototype, "customers", null);
|
||||||
exports2.AdminAgentsController = AdminAgentsController = __decorate2([
|
exports2.AdminAgentsController = AdminAgentsController = __decorate2([
|
||||||
|
(0, swagger_12.ApiTags)("AdminAgents"),
|
||||||
|
(0, swagger_12.ApiBearerAuth)(),
|
||||||
(0, common_12.Controller)("admin/agents"),
|
(0, common_12.Controller)("admin/agents"),
|
||||||
(0, auth_decorators_1.AuthType)("admin"),
|
(0, auth_decorators_1.AuthType)("admin"),
|
||||||
(0, auth_decorators_1.Roles)("admin", "sales"),
|
(0, auth_decorators_1.Roles)("admin", "sales"),
|
||||||
@@ -135063,6 +135074,8 @@ var require_admin_agents_controller = __commonJS({
|
|||||||
__metadata2("design:returntype", void 0)
|
__metadata2("design:returntype", void 0)
|
||||||
], AdminAssignmentRequestsController.prototype, "review", null);
|
], AdminAssignmentRequestsController.prototype, "review", null);
|
||||||
exports2.AdminAssignmentRequestsController = AdminAssignmentRequestsController = __decorate2([
|
exports2.AdminAssignmentRequestsController = AdminAssignmentRequestsController = __decorate2([
|
||||||
|
(0, swagger_12.ApiTags)("AdminAgents"),
|
||||||
|
(0, swagger_12.ApiBearerAuth)(),
|
||||||
(0, common_12.Controller)("admin/assignment-requests"),
|
(0, common_12.Controller)("admin/assignment-requests"),
|
||||||
(0, auth_decorators_1.AuthType)("admin"),
|
(0, auth_decorators_1.AuthType)("admin"),
|
||||||
(0, auth_decorators_1.Roles)("admin", "sales"),
|
(0, auth_decorators_1.Roles)("admin", "sales"),
|
||||||
@@ -142743,6 +142756,579 @@ var require_internal_messages_module = __commonJS({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// dist/montact/montact-auth.guard.js
|
||||||
|
var require_montact_auth_guard = __commonJS({
|
||||||
|
"dist/montact/montact-auth.guard.js"(exports2) {
|
||||||
|
"use strict";
|
||||||
|
var __decorate2 = exports2 && exports2.__decorate || function(decorators, target, key, desc) {
|
||||||
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||||
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||||
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||||
|
};
|
||||||
|
var __metadata2 = exports2 && exports2.__metadata || function(k, v) {
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
||||||
|
exports2.MontactAuthGuard = void 0;
|
||||||
|
var common_12 = require_common();
|
||||||
|
var config_1 = require_config2();
|
||||||
|
var jwt_1 = require_jwt();
|
||||||
|
var jwt_secret_1 = require_jwt_secret();
|
||||||
|
var MontactAuthGuard = class MontactAuthGuard {
|
||||||
|
static {
|
||||||
|
__name(this, "MontactAuthGuard");
|
||||||
|
}
|
||||||
|
jwt;
|
||||||
|
config;
|
||||||
|
constructor(jwt, config) {
|
||||||
|
this.jwt = jwt;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
async canActivate(context) {
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const authHeader = request.headers.authorization;
|
||||||
|
if (!authHeader?.startsWith("Bearer ")) {
|
||||||
|
throw new common_12.UnauthorizedException("\u062A\u0648\u06A9\u0646 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u06CC\u0627 \u0645\u0646\u0642\u0636\u06CC \u0634\u062F\u0647 \u0627\u0633\u062A");
|
||||||
|
}
|
||||||
|
const token = authHeader.slice(7).trim();
|
||||||
|
const montactToken = this.config.get("MONTACT_API_TOKEN")?.trim();
|
||||||
|
if (montactToken && token === montactToken) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const payload = await this.jwt.verifyAsync(token, {
|
||||||
|
secret: (0, jwt_secret_1.resolveJwtSecret)(this.config)
|
||||||
|
});
|
||||||
|
if (payload.type === "admin") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
throw new common_12.UnauthorizedException("\u062A\u0648\u06A9\u0646 \u0646\u0627\u0645\u0639\u062A\u0628\u0631 \u06CC\u0627 \u0645\u0646\u0642\u0636\u06CC \u0634\u062F\u0647 \u0627\u0633\u062A");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports2.MontactAuthGuard = MontactAuthGuard;
|
||||||
|
exports2.MontactAuthGuard = MontactAuthGuard = __decorate2([
|
||||||
|
(0, common_12.Injectable)(),
|
||||||
|
__metadata2("design:paramtypes", [
|
||||||
|
jwt_1.JwtService,
|
||||||
|
config_1.ConfigService
|
||||||
|
])
|
||||||
|
], MontactAuthGuard);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// dist/montact/montact.service.js
|
||||||
|
var require_montact_service = __commonJS({
|
||||||
|
"dist/montact/montact.service.js"(exports2) {
|
||||||
|
"use strict";
|
||||||
|
var __decorate2 = exports2 && exports2.__decorate || function(decorators, target, key, desc) {
|
||||||
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||||
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||||
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||||
|
};
|
||||||
|
var __metadata2 = exports2 && exports2.__metadata || function(k, v) {
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
||||||
|
exports2.MontactService = void 0;
|
||||||
|
var common_12 = require_common();
|
||||||
|
var client_1 = require_default2();
|
||||||
|
var config_1 = require_config2();
|
||||||
|
var prisma_service_1 = require_prisma_service();
|
||||||
|
var name_1 = require_name();
|
||||||
|
var MontactService = class MontactService {
|
||||||
|
static {
|
||||||
|
__name(this, "MontactService");
|
||||||
|
}
|
||||||
|
prisma;
|
||||||
|
config;
|
||||||
|
constructor(prisma, config) {
|
||||||
|
this.prisma = prisma;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
storeId() {
|
||||||
|
const raw2 = this.config.get("MONTACT_STORE_ID");
|
||||||
|
const n = raw2 ? Number.parseInt(raw2, 10) : 1;
|
||||||
|
return Number.isFinite(n) ? n : 1;
|
||||||
|
}
|
||||||
|
async listCustomers(page = 1, limit = 50, updatedAfter) {
|
||||||
|
const safePage = Math.max(1, page);
|
||||||
|
const safeLimit = Math.min(100, Math.max(1, limit));
|
||||||
|
const skip2 = (safePage - 1) * safeLimit;
|
||||||
|
const updatedFilter = updatedAfter ? { updatedAt: { gte: new Date(updatedAfter) } } : {};
|
||||||
|
const where = {
|
||||||
|
appRole: client_1.AppUserRole.customer,
|
||||||
|
...updatedFilter
|
||||||
|
};
|
||||||
|
const [total, rows] = await Promise.all([
|
||||||
|
this.prisma.customer.count({ where }),
|
||||||
|
this.prisma.customer.findMany({
|
||||||
|
where,
|
||||||
|
orderBy: { updatedAt: "desc" },
|
||||||
|
skip: skip2,
|
||||||
|
take: safeLimit,
|
||||||
|
include: {
|
||||||
|
orders: { select: { finalAmount: true } }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
const data = rows.map((c) => this.mapCustomerListItem(c, c.orders));
|
||||||
|
const lastPage = Math.max(1, Math.ceil(total / safeLimit));
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
meta: {
|
||||||
|
current_page: safePage,
|
||||||
|
per_page: safeLimit,
|
||||||
|
total,
|
||||||
|
last_page: lastPage
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async getCustomer(id) {
|
||||||
|
const customer = await this.prisma.customer.findFirst({
|
||||||
|
where: { id, appRole: client_1.AppUserRole.customer },
|
||||||
|
include: {
|
||||||
|
orders: {
|
||||||
|
orderBy: { createdAt: "desc" },
|
||||||
|
include: { items: true }
|
||||||
|
},
|
||||||
|
addresses: true,
|
||||||
|
favorites: { include: { product: { select: { id: true, name: true } } } }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!customer)
|
||||||
|
throw new common_12.NotFoundException("\u0645\u0646\u0628\u0639 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
|
const orders = customer.orders.map((o) => this.mapOrder(o, false));
|
||||||
|
const addresses = customer.addresses.map((a) => this.mapAddress(a, customer.id));
|
||||||
|
const summary = this.buildSummary(customer.orders);
|
||||||
|
return {
|
||||||
|
...this.mapCustomerBase(customer),
|
||||||
|
orders,
|
||||||
|
summary,
|
||||||
|
addresses,
|
||||||
|
transactions: [],
|
||||||
|
engagement: this.mapEngagement(customer.favorites)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async listCustomerOrders(customerId, updatedAfter) {
|
||||||
|
await this.assertCustomer(customerId);
|
||||||
|
const orders = await this.prisma.order.findMany({
|
||||||
|
where: {
|
||||||
|
userId: customerId,
|
||||||
|
...updatedAfter ? { updatedAt: { gte: new Date(updatedAfter) } } : {}
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: "desc" },
|
||||||
|
include: { items: true }
|
||||||
|
});
|
||||||
|
return { data: orders.map((o) => this.mapOrder(o, true)) };
|
||||||
|
}
|
||||||
|
async listCustomerAddresses(customerId) {
|
||||||
|
await this.assertCustomer(customerId);
|
||||||
|
const rows = await this.prisma.customerAddress.findMany({
|
||||||
|
where: { customerId },
|
||||||
|
orderBy: { createdAt: "desc" }
|
||||||
|
});
|
||||||
|
return { data: rows.map((a) => this.mapAddress(a, customerId)) };
|
||||||
|
}
|
||||||
|
async listCustomerTransactions(_customerId) {
|
||||||
|
await this.assertCustomer(_customerId);
|
||||||
|
return { data: [] };
|
||||||
|
}
|
||||||
|
async getCustomerEngagement(customerId) {
|
||||||
|
await this.assertCustomer(customerId);
|
||||||
|
const favorites = await this.prisma.productFavorite.findMany({
|
||||||
|
where: { customerId },
|
||||||
|
include: { product: { select: { id: true, name: true } } },
|
||||||
|
orderBy: { createdAt: "desc" }
|
||||||
|
});
|
||||||
|
return this.mapEngagement(favorites);
|
||||||
|
}
|
||||||
|
async getOrder(orderId) {
|
||||||
|
const order = await this.prisma.order.findUnique({
|
||||||
|
where: { id: orderId },
|
||||||
|
include: {
|
||||||
|
items: true,
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
phone: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!order)
|
||||||
|
throw new common_12.NotFoundException("\u0645\u0646\u0628\u0639 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
|
return this.mapOrder(order, true);
|
||||||
|
}
|
||||||
|
async assertCustomer(id) {
|
||||||
|
const c = await this.prisma.customer.findFirst({
|
||||||
|
where: { id, appRole: client_1.AppUserRole.customer },
|
||||||
|
select: { id: true }
|
||||||
|
});
|
||||||
|
if (!c)
|
||||||
|
throw new common_12.NotFoundException("\u0645\u0646\u0628\u0639 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
|
}
|
||||||
|
mapCustomerListItem(c, orders) {
|
||||||
|
const summary = this.buildSummary(orders);
|
||||||
|
return {
|
||||||
|
...this.mapCustomerBase(c),
|
||||||
|
orders: [],
|
||||||
|
summary,
|
||||||
|
addresses: [],
|
||||||
|
transactions: [],
|
||||||
|
engagement: { saved: [], likes: [], comments: [] }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mapCustomerBase(c) {
|
||||||
|
const name = (0, name_1.joinFullName)(c.firstName, c.lastName);
|
||||||
|
return {
|
||||||
|
id: c.id,
|
||||||
|
name,
|
||||||
|
first_name: c.firstName,
|
||||||
|
last_name: c.lastName,
|
||||||
|
phone: c.phone,
|
||||||
|
email: null,
|
||||||
|
username: c.phone,
|
||||||
|
avatar: c.avatarUrl,
|
||||||
|
birthday: null,
|
||||||
|
gender: null,
|
||||||
|
type: c.customerType === "retail" ? "individual" : "business",
|
||||||
|
store_id: this.storeId(),
|
||||||
|
created_at: c.createdAt.toISOString(),
|
||||||
|
updated_at: c.updatedAt.toISOString(),
|
||||||
|
description: c.businessName,
|
||||||
|
bio: null,
|
||||||
|
job: null,
|
||||||
|
cash_balance: 0,
|
||||||
|
gold_balance: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
buildSummary(orders) {
|
||||||
|
let totalSpent = 0;
|
||||||
|
for (const o of orders) {
|
||||||
|
totalSpent += o.finalAmount ?? 0;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
total_orders: orders.length,
|
||||||
|
total_spent: Math.round(totalSpent)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mapOrder(o, withNested) {
|
||||||
|
const totalPrice = o.finalAmount ?? o.totalAmount ?? 0;
|
||||||
|
const qty = o.items.length;
|
||||||
|
const base = {
|
||||||
|
id: o.id,
|
||||||
|
user_id: o.userId,
|
||||||
|
store_id: this.storeId(),
|
||||||
|
code: o.orderNumber,
|
||||||
|
sts: this.mapOrderStatus(o.status),
|
||||||
|
payment_sts: this.mapPaymentStatus(o.status),
|
||||||
|
payment_type: "unknown",
|
||||||
|
total_price: Math.round(totalPrice),
|
||||||
|
discount_total_price: Math.round(totalPrice),
|
||||||
|
total_discounts: Math.round(o.totalDiscount ?? 0),
|
||||||
|
total_tax: 0,
|
||||||
|
total_qty: qty,
|
||||||
|
profit: 0,
|
||||||
|
percent_profit: 0,
|
||||||
|
sum_gold: 0,
|
||||||
|
sum_silver: 0,
|
||||||
|
address: null,
|
||||||
|
address_id: null,
|
||||||
|
note: o.notes,
|
||||||
|
cancel_reason: null,
|
||||||
|
cancelled_at: null,
|
||||||
|
closed_at: null,
|
||||||
|
transaction_id: null,
|
||||||
|
code_discount: null,
|
||||||
|
discount_code_id: null,
|
||||||
|
created_at: o.createdAt.toISOString(),
|
||||||
|
updated_at: o.updatedAt.toISOString(),
|
||||||
|
deleted_at: null,
|
||||||
|
order_items: o.items.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
order_id: item.orderId,
|
||||||
|
product_id: item.productId ?? "",
|
||||||
|
product_name: item.productName,
|
||||||
|
qty: 1,
|
||||||
|
price: Math.round(item.unitPrice ?? 0),
|
||||||
|
weight: item.confirmedMeterage ?? item.requestedMeterage,
|
||||||
|
total_price: Math.round(item.lineTotal ?? 0)
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
if (withNested && o.user) {
|
||||||
|
base.user = {
|
||||||
|
id: o.user.id,
|
||||||
|
name: (0, name_1.joinFullName)(o.user.firstName, o.user.lastName),
|
||||||
|
phone: o.user.phone
|
||||||
|
};
|
||||||
|
base.store = { id: this.storeId() };
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
mapOrderStatus(status) {
|
||||||
|
if (status === client_1.OrderStatus.cancelled || status === client_1.OrderStatus.rejected) {
|
||||||
|
return "cancelled";
|
||||||
|
}
|
||||||
|
if (status === client_1.OrderStatus.delivered)
|
||||||
|
return "delivered";
|
||||||
|
if (status === client_1.OrderStatus.shipped || status === client_1.OrderStatus.ready_to_ship) {
|
||||||
|
return "shipped";
|
||||||
|
}
|
||||||
|
if (status === client_1.OrderStatus.final_approved || status === client_1.OrderStatus.preparing) {
|
||||||
|
return "processing";
|
||||||
|
}
|
||||||
|
return "pending";
|
||||||
|
}
|
||||||
|
mapPaymentStatus(status) {
|
||||||
|
if (status === client_1.OrderStatus.cash_confirmed || status === client_1.OrderStatus.checks_approved || status === client_1.OrderStatus.delivered) {
|
||||||
|
return "paid";
|
||||||
|
}
|
||||||
|
if (status === client_1.OrderStatus.cancelled || status === client_1.OrderStatus.rejected) {
|
||||||
|
return "cancelled";
|
||||||
|
}
|
||||||
|
return "unpaid";
|
||||||
|
}
|
||||||
|
mapAddress(a, userId) {
|
||||||
|
return {
|
||||||
|
id: a.id,
|
||||||
|
user_id: userId,
|
||||||
|
province: "",
|
||||||
|
city: "",
|
||||||
|
address: a.text,
|
||||||
|
postal_code: "",
|
||||||
|
receiver_name: a.label ?? "",
|
||||||
|
receiver_phone: "",
|
||||||
|
is_default: a.isDefault
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mapEngagement(favorites) {
|
||||||
|
return {
|
||||||
|
saved: favorites.map((f) => ({
|
||||||
|
product_id: f.product.id,
|
||||||
|
product_name: f.product.name,
|
||||||
|
saved_at: f.createdAt.toISOString()
|
||||||
|
})),
|
||||||
|
likes: [],
|
||||||
|
comments: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports2.MontactService = MontactService;
|
||||||
|
exports2.MontactService = MontactService = __decorate2([
|
||||||
|
(0, common_12.Injectable)(),
|
||||||
|
__metadata2("design:paramtypes", [
|
||||||
|
prisma_service_1.PrismaService,
|
||||||
|
config_1.ConfigService
|
||||||
|
])
|
||||||
|
], MontactService);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// dist/montact/montact.controller.js
|
||||||
|
var require_montact_controller = __commonJS({
|
||||||
|
"dist/montact/montact.controller.js"(exports2) {
|
||||||
|
"use strict";
|
||||||
|
var __decorate2 = exports2 && exports2.__decorate || function(decorators, target, key, desc) {
|
||||||
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||||
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||||
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||||
|
};
|
||||||
|
var __metadata2 = exports2 && exports2.__metadata || function(k, v) {
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||||
|
};
|
||||||
|
var __param2 = exports2 && exports2.__param || function(paramIndex, decorator) {
|
||||||
|
return function(target, key) {
|
||||||
|
decorator(target, key, paramIndex);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
||||||
|
exports2.MontactController = void 0;
|
||||||
|
var common_12 = require_common();
|
||||||
|
var swagger_12 = require_dist6();
|
||||||
|
var auth_decorators_1 = require_auth_decorators();
|
||||||
|
var montact_auth_guard_1 = require_montact_auth_guard();
|
||||||
|
var montact_service_1 = require_montact_service();
|
||||||
|
var MontactController = class MontactController {
|
||||||
|
static {
|
||||||
|
__name(this, "MontactController");
|
||||||
|
}
|
||||||
|
montact;
|
||||||
|
constructor(montact) {
|
||||||
|
this.montact = montact;
|
||||||
|
}
|
||||||
|
listCustomers(page, limit, updatedAfter) {
|
||||||
|
const p = page ? Number.parseInt(page, 10) : 1;
|
||||||
|
const l = limit ? Number.parseInt(limit, 10) : 50;
|
||||||
|
return this.montact.listCustomers(p, l, updatedAfter);
|
||||||
|
}
|
||||||
|
createCustomer() {
|
||||||
|
throw new common_12.NotImplementedException("\u062B\u0628\u062A \u0645\u0634\u062A\u0631\u06CC \u0627\u0632 CRM \u062F\u0631 \u0627\u06CC\u0646 \u0641\u0627\u0632 \u0641\u0639\u0627\u0644 \u0646\u06CC\u0633\u062A");
|
||||||
|
}
|
||||||
|
getCustomer(id) {
|
||||||
|
return this.montact.getCustomer(id);
|
||||||
|
}
|
||||||
|
customerOrders(id, updatedAfter) {
|
||||||
|
return this.montact.listCustomerOrders(id, updatedAfter);
|
||||||
|
}
|
||||||
|
createOrder() {
|
||||||
|
throw new common_12.NotImplementedException("\u062B\u0628\u062A \u0633\u0641\u0627\u0631\u0634 \u0627\u0632 CRM \u062F\u0631 \u0627\u06CC\u0646 \u0641\u0627\u0632 \u0641\u0639\u0627\u0644 \u0646\u06CC\u0633\u062A");
|
||||||
|
}
|
||||||
|
customerAddresses(id) {
|
||||||
|
return this.montact.listCustomerAddresses(id);
|
||||||
|
}
|
||||||
|
customerTransactions(id) {
|
||||||
|
return this.montact.listCustomerTransactions(id);
|
||||||
|
}
|
||||||
|
customerEngagement(id) {
|
||||||
|
return this.montact.getCustomerEngagement(id);
|
||||||
|
}
|
||||||
|
getOrder(orderId) {
|
||||||
|
return this.montact.getOrder(orderId);
|
||||||
|
}
|
||||||
|
registerWebhook() {
|
||||||
|
throw new common_12.NotImplementedException("Webhook Montact \u062F\u0631 \u0641\u0627\u0632 \u06F3 \u067E\u06CC\u0627\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F");
|
||||||
|
}
|
||||||
|
getWebhook() {
|
||||||
|
throw new common_12.NotImplementedException("Webhook Montact \u062F\u0631 \u0641\u0627\u0632 \u06F3 \u067E\u06CC\u0627\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F");
|
||||||
|
}
|
||||||
|
deleteWebhook() {
|
||||||
|
throw new common_12.NotImplementedException("Webhook Montact \u062F\u0631 \u0641\u0627\u0632 \u06F3 \u067E\u06CC\u0627\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports2.MontactController = MontactController;
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Get)("customers"),
|
||||||
|
__param2(0, (0, common_12.Query)("page")),
|
||||||
|
__param2(1, (0, common_12.Query)("limit")),
|
||||||
|
__param2(2, (0, common_12.Query)("updated_after")),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", [String, String, String]),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "listCustomers", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Post)("customers"),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", []),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "createCustomer", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Get)("customers/:id"),
|
||||||
|
__param2(0, (0, common_12.Param)("id")),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", [String]),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "getCustomer", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Get)("customers/:id/orders"),
|
||||||
|
__param2(0, (0, common_12.Param)("id")),
|
||||||
|
__param2(1, (0, common_12.Query)("updated_after")),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", [String, String]),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "customerOrders", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Post)("customers/:id/orders"),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", []),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "createOrder", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Get)("customers/:id/addresses"),
|
||||||
|
__param2(0, (0, common_12.Param)("id")),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", [String]),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "customerAddresses", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Get)("customers/:id/transactions"),
|
||||||
|
__param2(0, (0, common_12.Param)("id")),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", [String]),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "customerTransactions", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Get)("customers/:id/engagement"),
|
||||||
|
__param2(0, (0, common_12.Param)("id")),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", [String]),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "customerEngagement", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Get)("orders/:orderId"),
|
||||||
|
__param2(0, (0, common_12.Param)("orderId")),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", [String]),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "getOrder", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Post)("webhooks/register"),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", []),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "registerWebhook", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Get)("webhooks/:id"),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", []),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "getWebhook", null);
|
||||||
|
__decorate2([
|
||||||
|
(0, common_12.Delete)("webhooks/:id"),
|
||||||
|
__metadata2("design:type", Function),
|
||||||
|
__metadata2("design:paramtypes", []),
|
||||||
|
__metadata2("design:returntype", void 0)
|
||||||
|
], MontactController.prototype, "deleteWebhook", null);
|
||||||
|
exports2.MontactController = MontactController = __decorate2([
|
||||||
|
(0, auth_decorators_1.Public)(),
|
||||||
|
(0, swagger_12.ApiTags)("Montact"),
|
||||||
|
(0, swagger_12.ApiBearerAuth)(),
|
||||||
|
(0, common_12.UseGuards)(montact_auth_guard_1.MontactAuthGuard),
|
||||||
|
(0, common_12.Controller)("montact"),
|
||||||
|
__metadata2("design:paramtypes", [montact_service_1.MontactService])
|
||||||
|
], MontactController);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// dist/montact/montact.module.js
|
||||||
|
var require_montact_module = __commonJS({
|
||||||
|
"dist/montact/montact.module.js"(exports2) {
|
||||||
|
"use strict";
|
||||||
|
var __decorate2 = exports2 && exports2.__decorate || function(decorators, target, key, desc) {
|
||||||
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||||
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||||
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||||
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
||||||
|
exports2.MontactModule = void 0;
|
||||||
|
var common_12 = require_common();
|
||||||
|
var auth_module_1 = require_auth_module();
|
||||||
|
var montact_controller_1 = require_montact_controller();
|
||||||
|
var montact_service_1 = require_montact_service();
|
||||||
|
var montact_auth_guard_1 = require_montact_auth_guard();
|
||||||
|
var MontactModule = class MontactModule {
|
||||||
|
static {
|
||||||
|
__name(this, "MontactModule");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports2.MontactModule = MontactModule;
|
||||||
|
exports2.MontactModule = MontactModule = __decorate2([
|
||||||
|
(0, common_12.Module)({
|
||||||
|
imports: [auth_module_1.AuthModule],
|
||||||
|
controllers: [montact_controller_1.MontactController],
|
||||||
|
providers: [montact_service_1.MontactService, montact_auth_guard_1.MontactAuthGuard]
|
||||||
|
})
|
||||||
|
], MontactModule);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// dist/app.module.js
|
// dist/app.module.js
|
||||||
var require_app_module = __commonJS({
|
var require_app_module = __commonJS({
|
||||||
"dist/app.module.js"(exports2) {
|
"dist/app.module.js"(exports2) {
|
||||||
@@ -142782,6 +143368,7 @@ var require_app_module = __commonJS({
|
|||||||
var addresses_module_1 = require_addresses_module();
|
var addresses_module_1 = require_addresses_module();
|
||||||
var internal_messages_module_1 = require_internal_messages_module();
|
var internal_messages_module_1 = require_internal_messages_module();
|
||||||
var crm_module_1 = require_crm_module();
|
var crm_module_1 = require_crm_module();
|
||||||
|
var montact_module_1 = require_montact_module();
|
||||||
var AppModule = class AppModule {
|
var AppModule = class AppModule {
|
||||||
static {
|
static {
|
||||||
__name(this, "AppModule");
|
__name(this, "AppModule");
|
||||||
@@ -142814,7 +143401,8 @@ var require_app_module = __commonJS({
|
|||||||
banners_module_1.BannersModule,
|
banners_module_1.BannersModule,
|
||||||
addresses_module_1.AddressesModule,
|
addresses_module_1.AddressesModule,
|
||||||
internal_messages_module_1.InternalMessagesModule,
|
internal_messages_module_1.InternalMessagesModule,
|
||||||
crm_module_1.CrmModule
|
crm_module_1.CrmModule,
|
||||||
|
montact_module_1.MontactModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
@@ -143067,8 +143655,10 @@ async function bootstrap() {
|
|||||||
}));
|
}));
|
||||||
const swaggerConfig = new swagger_1.DocumentBuilder().setTitle("Fabric Trade API").setDescription("API \u0633\u0631\u0648\u0631 \u062A\u062C\u0627\u0631\u062A \u067E\u0627\u0631\u0686\u0647").setVersion("1.0").addBearerAuth().build();
|
const swaggerConfig = new swagger_1.DocumentBuilder().setTitle("Fabric Trade API").setDescription("API \u0633\u0631\u0648\u0631 \u062A\u062C\u0627\u0631\u062A \u067E\u0627\u0631\u0686\u0647").setVersion("1.0").addBearerAuth().build();
|
||||||
const document2 = swagger_1.SwaggerModule.createDocument(app, swaggerConfig);
|
const document2 = swagger_1.SwaggerModule.createDocument(app, swaggerConfig);
|
||||||
|
document2.security = [{ bearer: [] }];
|
||||||
swagger_1.SwaggerModule.setup(`${globalPrefix}/docs`, app, document2, {
|
swagger_1.SwaggerModule.setup(`${globalPrefix}/docs`, app, document2, {
|
||||||
customSwaggerUiPath: swaggerUiAssetsPath()
|
customSwaggerUiPath: swaggerUiAssetsPath(),
|
||||||
|
swaggerOptions: { persistAuthorization: true }
|
||||||
});
|
});
|
||||||
const port = Number(process.env.PORT) || 3e3;
|
const port = Number(process.env.PORT) || 3e3;
|
||||||
await app.listen(port);
|
await app.listen(port);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { BannersModule } from './banners/banners.module';
|
|||||||
import { AddressesModule } from './addresses/addresses.module';
|
import { AddressesModule } from './addresses/addresses.module';
|
||||||
import { InternalMessagesModule } from './internal-messages/internal-messages.module';
|
import { InternalMessagesModule } from './internal-messages/internal-messages.module';
|
||||||
import { CrmModule } from './crm/crm.module';
|
import { CrmModule } from './crm/crm.module';
|
||||||
|
import { MontactModule } from './montact/montact.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -52,6 +53,7 @@ import { CrmModule } from './crm/crm.module';
|
|||||||
AddressesModule,
|
AddressesModule,
|
||||||
InternalMessagesModule,
|
InternalMessagesModule,
|
||||||
CrmModule,
|
CrmModule,
|
||||||
|
MontactModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Query,
|
Query,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||||
import { AssignmentRequestStatus } from '@prisma/client';
|
import { AssignmentRequestStatus } from '@prisma/client';
|
||||||
import { AuthType, Roles } from '../common/decorators/auth.decorators';
|
import { AuthType, Roles } from '../common/decorators/auth.decorators';
|
||||||
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
||||||
@@ -18,6 +19,8 @@ import {
|
|||||||
UpdateAdminAgentDto,
|
UpdateAdminAgentDto,
|
||||||
} from './dto/crm.dto';
|
} from './dto/crm.dto';
|
||||||
|
|
||||||
|
@ApiTags('AdminAgents')
|
||||||
|
@ApiBearerAuth()
|
||||||
@Controller('admin/agents')
|
@Controller('admin/agents')
|
||||||
@AuthType('admin')
|
@AuthType('admin')
|
||||||
@Roles('admin', 'sales')
|
@Roles('admin', 'sales')
|
||||||
@@ -45,6 +48,8 @@ export class AdminAgentsController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiTags('AdminAgents')
|
||||||
|
@ApiBearerAuth()
|
||||||
@Controller('admin/assignment-requests')
|
@Controller('admin/assignment-requests')
|
||||||
@AuthType('admin')
|
@AuthType('admin')
|
||||||
@Roles('admin', 'sales')
|
@Roles('admin', 'sales')
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { Controller, Get, Param, Patch, Body, Query } from '@nestjs/common';
|
import { Controller, Get, Param, Patch, Body, Query } from '@nestjs/common';
|
||||||
|
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||||
import { SalesPipelineStage } from '@prisma/client';
|
import { SalesPipelineStage } from '@prisma/client';
|
||||||
import { AuthType, Roles } from '../common/decorators/auth.decorators';
|
import { AuthType, Roles } from '../common/decorators/auth.decorators';
|
||||||
import { CrmService } from './crm.service';
|
import { CrmService } from './crm.service';
|
||||||
import { UpdateOpportunityDto } from './dto/crm.dto';
|
import { UpdateOpportunityDto } from './dto/crm.dto';
|
||||||
|
|
||||||
|
@ApiTags('Crm')
|
||||||
|
@ApiBearerAuth()
|
||||||
@Controller('admin/crm')
|
@Controller('admin/crm')
|
||||||
@AuthType('admin')
|
@AuthType('admin')
|
||||||
@Roles('admin', 'sales')
|
@Roles('admin', 'sales')
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
Patch,
|
Patch,
|
||||||
Post,
|
Post,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||||
import { AuthType, Roles } from '../common/decorators/auth.decorators';
|
import { AuthType, Roles } from '../common/decorators/auth.decorators';
|
||||||
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
||||||
import type { AuthPrincipal } from '../common/decorators/current-user.decorator';
|
import type { AuthPrincipal } from '../common/decorators/current-user.decorator';
|
||||||
@@ -18,6 +19,8 @@ import {
|
|||||||
UpdateNextActionDto,
|
UpdateNextActionDto,
|
||||||
} from './dto/crm.dto';
|
} from './dto/crm.dto';
|
||||||
|
|
||||||
|
@ApiTags('CustomerCrm')
|
||||||
|
@ApiBearerAuth()
|
||||||
@Controller('admin/customers')
|
@Controller('admin/customers')
|
||||||
@AuthType('admin')
|
@AuthType('admin')
|
||||||
@Roles('admin', 'sales')
|
@Roles('admin', 'sales')
|
||||||
@@ -68,6 +71,8 @@ export class CustomerCrmController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiTags('CustomerCrm')
|
||||||
|
@ApiBearerAuth()
|
||||||
@Controller('admin/next-actions')
|
@Controller('admin/next-actions')
|
||||||
@AuthType('admin')
|
@AuthType('admin')
|
||||||
@Roles('admin', 'sales')
|
@Roles('admin', 'sales')
|
||||||
|
|||||||
@@ -56,8 +56,11 @@ async function bootstrap() {
|
|||||||
.addBearerAuth()
|
.addBearerAuth()
|
||||||
.build();
|
.build();
|
||||||
const document = SwaggerModule.createDocument(app, swaggerConfig);
|
const document = SwaggerModule.createDocument(app, swaggerConfig);
|
||||||
|
// تا دکمه Authorize توکن را به همه endpointها (مثل admin/crm) بفرستد
|
||||||
|
document.security = [{ bearer: [] }];
|
||||||
SwaggerModule.setup(`${globalPrefix}/docs`, app, document, {
|
SwaggerModule.setup(`${globalPrefix}/docs`, app, document, {
|
||||||
customSwaggerUiPath: swaggerUiAssetsPath(),
|
customSwaggerUiPath: swaggerUiAssetsPath(),
|
||||||
|
swaggerOptions: { persistAuthorization: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
const port = Number(process.env.PORT) || 3000;
|
const port = Number(process.env.PORT) || 3000;
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import {
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
Injectable,
|
||||||
|
UnauthorizedException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { resolveJwtSecret } from '../common/utils/jwt-secret';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MontactAuthGuard implements CanActivate {
|
||||||
|
constructor(
|
||||||
|
private readonly jwt: JwtService,
|
||||||
|
private readonly config: ConfigService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
|
const request = context.switchToHttp().getRequest<{
|
||||||
|
headers: Record<string, string | undefined>;
|
||||||
|
}>();
|
||||||
|
const authHeader = request.headers.authorization;
|
||||||
|
if (!authHeader?.startsWith('Bearer ')) {
|
||||||
|
throw new UnauthorizedException('توکن نامعتبر یا منقضی شده است');
|
||||||
|
}
|
||||||
|
const token = authHeader.slice(7).trim();
|
||||||
|
const montactToken = this.config.get<string>('MONTACT_API_TOKEN')?.trim();
|
||||||
|
if (montactToken && token === montactToken) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const payload = await this.jwt.verifyAsync<{ type?: string }>(token, {
|
||||||
|
secret: resolveJwtSecret(this.config),
|
||||||
|
});
|
||||||
|
if (payload.type === 'admin') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
throw new UnauthorizedException('توکن نامعتبر یا منقضی شده است');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
NotImplementedException,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { Public } from '../common/decorators/auth.decorators';
|
||||||
|
import { MontactAuthGuard } from './montact-auth.guard';
|
||||||
|
import { MontactService } from './montact.service';
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@ApiTags('Montact')
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(MontactAuthGuard)
|
||||||
|
@Controller('montact')
|
||||||
|
export class MontactController {
|
||||||
|
constructor(private readonly montact: MontactService) {}
|
||||||
|
|
||||||
|
@Get('customers')
|
||||||
|
listCustomers(
|
||||||
|
@Query('page') page?: string,
|
||||||
|
@Query('limit') limit?: string,
|
||||||
|
@Query('updated_after') updatedAfter?: string,
|
||||||
|
) {
|
||||||
|
const p = page ? Number.parseInt(page, 10) : 1;
|
||||||
|
const l = limit ? Number.parseInt(limit, 10) : 50;
|
||||||
|
return this.montact.listCustomers(p, l, updatedAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('customers')
|
||||||
|
createCustomer() {
|
||||||
|
throw new NotImplementedException('ثبت مشتری از CRM در این فاز فعال نیست');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('customers/:id')
|
||||||
|
getCustomer(@Param('id') id: string) {
|
||||||
|
return this.montact.getCustomer(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('customers/:id/orders')
|
||||||
|
customerOrders(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Query('updated_after') updatedAfter?: string,
|
||||||
|
) {
|
||||||
|
return this.montact.listCustomerOrders(id, updatedAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('customers/:id/orders')
|
||||||
|
createOrder() {
|
||||||
|
throw new NotImplementedException('ثبت سفارش از CRM در این فاز فعال نیست');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('customers/:id/addresses')
|
||||||
|
customerAddresses(@Param('id') id: string) {
|
||||||
|
return this.montact.listCustomerAddresses(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('customers/:id/transactions')
|
||||||
|
customerTransactions(@Param('id') id: string) {
|
||||||
|
return this.montact.listCustomerTransactions(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('customers/:id/engagement')
|
||||||
|
customerEngagement(@Param('id') id: string) {
|
||||||
|
return this.montact.getCustomerEngagement(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('orders/:orderId')
|
||||||
|
getOrder(@Param('orderId') orderId: string) {
|
||||||
|
return this.montact.getOrder(orderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('webhooks/register')
|
||||||
|
registerWebhook() {
|
||||||
|
throw new NotImplementedException('Webhook Montact در فاز ۳ پیاده میشود');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('webhooks/:id')
|
||||||
|
getWebhook() {
|
||||||
|
throw new NotImplementedException('Webhook Montact در فاز ۳ پیاده میشود');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete('webhooks/:id')
|
||||||
|
deleteWebhook() {
|
||||||
|
throw new NotImplementedException('Webhook Montact در فاز ۳ پیاده میشود');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AuthModule } from '../auth/auth.module';
|
||||||
|
import { MontactController } from './montact.controller';
|
||||||
|
import { MontactService } from './montact.service';
|
||||||
|
import { MontactAuthGuard } from './montact-auth.guard';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [AuthModule],
|
||||||
|
controllers: [MontactController],
|
||||||
|
providers: [MontactService, MontactAuthGuard],
|
||||||
|
})
|
||||||
|
export class MontactModule {}
|
||||||
@@ -0,0 +1,397 @@
|
|||||||
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
|
import { AppUserRole, OrderStatus, Prisma } from '@prisma/client';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { joinFullName } from '../common/utils/name';
|
||||||
|
|
||||||
|
type MontactCustomer = Record<string, unknown>;
|
||||||
|
type MontactOrder = Record<string, unknown>;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MontactService {
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly config: ConfigService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private storeId(): number {
|
||||||
|
const raw = this.config.get<string>('MONTACT_STORE_ID');
|
||||||
|
const n = raw ? Number.parseInt(raw, 10) : 1;
|
||||||
|
return Number.isFinite(n) ? n : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
async listCustomers(
|
||||||
|
page = 1,
|
||||||
|
limit = 50,
|
||||||
|
updatedAfter?: string,
|
||||||
|
) {
|
||||||
|
const safePage = Math.max(1, page);
|
||||||
|
const safeLimit = Math.min(100, Math.max(1, limit));
|
||||||
|
const skip = (safePage - 1) * safeLimit;
|
||||||
|
|
||||||
|
const updatedFilter = updatedAfter
|
||||||
|
? { updatedAt: { gte: new Date(updatedAfter) } }
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const where: Prisma.CustomerWhereInput = {
|
||||||
|
appRole: AppUserRole.customer,
|
||||||
|
...updatedFilter,
|
||||||
|
};
|
||||||
|
|
||||||
|
const [total, rows] = await Promise.all([
|
||||||
|
this.prisma.customer.count({ where }),
|
||||||
|
this.prisma.customer.findMany({
|
||||||
|
where,
|
||||||
|
orderBy: { updatedAt: 'desc' },
|
||||||
|
skip,
|
||||||
|
take: safeLimit,
|
||||||
|
include: {
|
||||||
|
orders: { select: { finalAmount: true } },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const data = rows.map((c) =>
|
||||||
|
this.mapCustomerListItem(c, c.orders),
|
||||||
|
);
|
||||||
|
const lastPage = Math.max(1, Math.ceil(total / safeLimit));
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
meta: {
|
||||||
|
current_page: safePage,
|
||||||
|
per_page: safeLimit,
|
||||||
|
total,
|
||||||
|
last_page: lastPage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCustomer(id: string) {
|
||||||
|
const customer = await this.prisma.customer.findFirst({
|
||||||
|
where: { id, appRole: AppUserRole.customer },
|
||||||
|
include: {
|
||||||
|
orders: {
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
include: { items: true },
|
||||||
|
},
|
||||||
|
addresses: true,
|
||||||
|
favorites: { include: { product: { select: { id: true, name: true } } } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!customer) throw new NotFoundException('منبع یافت نشد');
|
||||||
|
|
||||||
|
const orders = customer.orders.map((o) => this.mapOrder(o, false));
|
||||||
|
const addresses = customer.addresses.map((a) => this.mapAddress(a, customer.id));
|
||||||
|
const summary = this.buildSummary(customer.orders);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...this.mapCustomerBase(customer),
|
||||||
|
orders,
|
||||||
|
summary,
|
||||||
|
addresses,
|
||||||
|
transactions: [],
|
||||||
|
engagement: this.mapEngagement(customer.favorites),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async listCustomerOrders(
|
||||||
|
customerId: string,
|
||||||
|
updatedAfter?: string,
|
||||||
|
) {
|
||||||
|
await this.assertCustomer(customerId);
|
||||||
|
const orders = await this.prisma.order.findMany({
|
||||||
|
where: {
|
||||||
|
userId: customerId,
|
||||||
|
...(updatedAfter
|
||||||
|
? { updatedAt: { gte: new Date(updatedAfter) } }
|
||||||
|
: {}),
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
include: { items: true },
|
||||||
|
});
|
||||||
|
return { data: orders.map((o) => this.mapOrder(o, true)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
async listCustomerAddresses(customerId: string) {
|
||||||
|
await this.assertCustomer(customerId);
|
||||||
|
const rows = await this.prisma.customerAddress.findMany({
|
||||||
|
where: { customerId },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
});
|
||||||
|
return { data: rows.map((a) => this.mapAddress(a, customerId)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
async listCustomerTransactions(_customerId: string) {
|
||||||
|
await this.assertCustomer(_customerId);
|
||||||
|
return { data: [] as unknown[] };
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCustomerEngagement(customerId: string) {
|
||||||
|
await this.assertCustomer(customerId);
|
||||||
|
const favorites = await this.prisma.productFavorite.findMany({
|
||||||
|
where: { customerId },
|
||||||
|
include: { product: { select: { id: true, name: true } } },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
});
|
||||||
|
return this.mapEngagement(favorites);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOrder(orderId: string) {
|
||||||
|
const order = await this.prisma.order.findUnique({
|
||||||
|
where: { id: orderId },
|
||||||
|
include: {
|
||||||
|
items: true,
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
phone: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!order) throw new NotFoundException('منبع یافت نشد');
|
||||||
|
return this.mapOrder(order, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async assertCustomer(id: string) {
|
||||||
|
const c = await this.prisma.customer.findFirst({
|
||||||
|
where: { id, appRole: AppUserRole.customer },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
if (!c) throw new NotFoundException('منبع یافت نشد');
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapCustomerListItem(
|
||||||
|
c: {
|
||||||
|
id: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
phone: string;
|
||||||
|
customerType: string;
|
||||||
|
avatarUrl: string | null;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
businessName: string | null;
|
||||||
|
},
|
||||||
|
orders: Array<{ finalAmount: number | null }>,
|
||||||
|
): MontactCustomer {
|
||||||
|
const summary = this.buildSummary(orders);
|
||||||
|
return {
|
||||||
|
...this.mapCustomerBase(c),
|
||||||
|
orders: [],
|
||||||
|
summary,
|
||||||
|
addresses: [],
|
||||||
|
transactions: [],
|
||||||
|
engagement: { saved: [], likes: [], comments: [] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapCustomerBase(c: {
|
||||||
|
id: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
phone: string;
|
||||||
|
customerType: string;
|
||||||
|
avatarUrl: string | null;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
businessName: string | null;
|
||||||
|
}): MontactCustomer {
|
||||||
|
const name = joinFullName(c.firstName, c.lastName);
|
||||||
|
return {
|
||||||
|
id: c.id,
|
||||||
|
name,
|
||||||
|
first_name: c.firstName,
|
||||||
|
last_name: c.lastName,
|
||||||
|
phone: c.phone,
|
||||||
|
email: null,
|
||||||
|
username: c.phone,
|
||||||
|
avatar: c.avatarUrl,
|
||||||
|
birthday: null,
|
||||||
|
gender: null,
|
||||||
|
type: c.customerType === 'retail' ? 'individual' : 'business',
|
||||||
|
store_id: this.storeId(),
|
||||||
|
created_at: c.createdAt.toISOString(),
|
||||||
|
updated_at: c.updatedAt.toISOString(),
|
||||||
|
description: c.businessName,
|
||||||
|
bio: null,
|
||||||
|
job: null,
|
||||||
|
cash_balance: 0,
|
||||||
|
gold_balance: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildSummary(
|
||||||
|
orders: Array<{ finalAmount: number | null }>,
|
||||||
|
) {
|
||||||
|
let totalSpent = 0;
|
||||||
|
for (const o of orders) {
|
||||||
|
totalSpent += o.finalAmount ?? 0;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
total_orders: orders.length,
|
||||||
|
total_spent: Math.round(totalSpent),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapOrder(
|
||||||
|
o: {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
orderNumber: string;
|
||||||
|
status: OrderStatus;
|
||||||
|
finalAmount: number | null;
|
||||||
|
totalAmount: number | null;
|
||||||
|
totalDiscount: number | null;
|
||||||
|
totalMeterage: number;
|
||||||
|
notes: string | null;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
items: Array<{
|
||||||
|
id: string;
|
||||||
|
orderId: string;
|
||||||
|
productId: string | null;
|
||||||
|
productName: string;
|
||||||
|
unitPrice: number | null;
|
||||||
|
lineTotal: number | null;
|
||||||
|
confirmedMeterage: number | null;
|
||||||
|
requestedMeterage: number;
|
||||||
|
}>;
|
||||||
|
user?: {
|
||||||
|
id: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
phone: string;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
withNested: boolean,
|
||||||
|
): MontactOrder {
|
||||||
|
const totalPrice = o.finalAmount ?? o.totalAmount ?? 0;
|
||||||
|
const qty = o.items.length;
|
||||||
|
const base: MontactOrder = {
|
||||||
|
id: o.id,
|
||||||
|
user_id: o.userId,
|
||||||
|
store_id: this.storeId(),
|
||||||
|
code: o.orderNumber,
|
||||||
|
sts: this.mapOrderStatus(o.status),
|
||||||
|
payment_sts: this.mapPaymentStatus(o.status),
|
||||||
|
payment_type: 'unknown',
|
||||||
|
total_price: Math.round(totalPrice),
|
||||||
|
discount_total_price: Math.round(totalPrice),
|
||||||
|
total_discounts: Math.round(o.totalDiscount ?? 0),
|
||||||
|
total_tax: 0,
|
||||||
|
total_qty: qty,
|
||||||
|
profit: 0,
|
||||||
|
percent_profit: 0,
|
||||||
|
sum_gold: 0,
|
||||||
|
sum_silver: 0,
|
||||||
|
address: null,
|
||||||
|
address_id: null,
|
||||||
|
note: o.notes,
|
||||||
|
cancel_reason: null,
|
||||||
|
cancelled_at: null,
|
||||||
|
closed_at: null,
|
||||||
|
transaction_id: null,
|
||||||
|
code_discount: null,
|
||||||
|
discount_code_id: null,
|
||||||
|
created_at: o.createdAt.toISOString(),
|
||||||
|
updated_at: o.updatedAt.toISOString(),
|
||||||
|
deleted_at: null,
|
||||||
|
order_items: o.items.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
order_id: item.orderId,
|
||||||
|
product_id: item.productId ?? '',
|
||||||
|
product_name: item.productName,
|
||||||
|
qty: 1,
|
||||||
|
price: Math.round(item.unitPrice ?? 0),
|
||||||
|
weight: item.confirmedMeterage ?? item.requestedMeterage,
|
||||||
|
total_price: Math.round(item.lineTotal ?? 0),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
if (withNested && o.user) {
|
||||||
|
base.user = {
|
||||||
|
id: o.user.id,
|
||||||
|
name: joinFullName(o.user.firstName, o.user.lastName),
|
||||||
|
phone: o.user.phone,
|
||||||
|
};
|
||||||
|
base.store = { id: this.storeId() };
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapOrderStatus(status: OrderStatus): string {
|
||||||
|
if (status === OrderStatus.cancelled || status === OrderStatus.rejected) {
|
||||||
|
return 'cancelled';
|
||||||
|
}
|
||||||
|
if (status === OrderStatus.delivered) return 'delivered';
|
||||||
|
if (
|
||||||
|
status === OrderStatus.shipped ||
|
||||||
|
status === OrderStatus.ready_to_ship
|
||||||
|
) {
|
||||||
|
return 'shipped';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
status === OrderStatus.final_approved ||
|
||||||
|
status === OrderStatus.preparing
|
||||||
|
) {
|
||||||
|
return 'processing';
|
||||||
|
}
|
||||||
|
return 'pending';
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapPaymentStatus(status: OrderStatus): string {
|
||||||
|
if (
|
||||||
|
status === OrderStatus.cash_confirmed ||
|
||||||
|
status === OrderStatus.checks_approved ||
|
||||||
|
status === OrderStatus.delivered
|
||||||
|
) {
|
||||||
|
return 'paid';
|
||||||
|
}
|
||||||
|
if (status === OrderStatus.cancelled || status === OrderStatus.rejected) {
|
||||||
|
return 'cancelled';
|
||||||
|
}
|
||||||
|
return 'unpaid';
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapAddress(
|
||||||
|
a: {
|
||||||
|
id: string;
|
||||||
|
label: string | null;
|
||||||
|
text: string;
|
||||||
|
isDefault: boolean;
|
||||||
|
},
|
||||||
|
userId: string,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
id: a.id,
|
||||||
|
user_id: userId,
|
||||||
|
province: '',
|
||||||
|
city: '',
|
||||||
|
address: a.text,
|
||||||
|
postal_code: '',
|
||||||
|
receiver_name: a.label ?? '',
|
||||||
|
receiver_phone: '',
|
||||||
|
is_default: a.isDefault,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapEngagement(
|
||||||
|
favorites: Array<{
|
||||||
|
createdAt: Date;
|
||||||
|
product: { id: string; name: string };
|
||||||
|
}>,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
saved: favorites.map((f) => ({
|
||||||
|
product_id: f.product.id,
|
||||||
|
product_name: f.product.name,
|
||||||
|
saved_at: f.createdAt.toISOString(),
|
||||||
|
})),
|
||||||
|
likes: [] as unknown[],
|
||||||
|
comments: [] as unknown[],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user