tt-entities 0.0.55 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/libs/tatayab-entities-library/entities/order-edit-item.entity.d.ts +24 -0
  2. package/dist/libs/tatayab-entities-library/entities/order-edit-item.entity.js +98 -0
  3. package/dist/libs/tatayab-entities-library/entities/order-edit-item.entity.js.map +1 -0
  4. package/dist/libs/tatayab-entities-library/entities/order-edit-session.entity.d.ts +10 -0
  5. package/dist/libs/tatayab-entities-library/entities/order-edit-session.entity.js +43 -0
  6. package/dist/libs/tatayab-entities-library/entities/order-edit-session.entity.js.map +1 -0
  7. package/dist/libs/tatayab-entities-library/entities/order-item-change-log.entity.d.ts +22 -0
  8. package/dist/libs/tatayab-entities-library/entities/order-item-change-log.entity.js +77 -0
  9. package/dist/libs/tatayab-entities-library/entities/order-item-change-log.entity.js.map +1 -0
  10. package/dist/libs/tatayab-entities-library/entities/order-item.entity.d.ts +1 -0
  11. package/dist/libs/tatayab-entities-library/entities/order-item.entity.js +4 -0
  12. package/dist/libs/tatayab-entities-library/entities/order-item.entity.js.map +1 -1
  13. package/dist/libs/tatayab-entities-library/entities/order-payment.entity.d.ts +28 -0
  14. package/dist/libs/tatayab-entities-library/entities/order-payment.entity.js +95 -0
  15. package/dist/libs/tatayab-entities-library/entities/order-payment.entity.js.map +1 -0
  16. package/dist/libs/tatayab-entities-library/entities/order.entity.d.ts +7 -0
  17. package/dist/libs/tatayab-entities-library/entities/order.entity.js +19 -0
  18. package/dist/libs/tatayab-entities-library/entities/order.entity.js.map +1 -1
  19. package/dist/libs/tatayab-entities-library/entities/refund.entity.d.ts +8 -0
  20. package/dist/libs/tatayab-entities-library/entities/refund.entity.js +24 -1
  21. package/dist/libs/tatayab-entities-library/entities/refund.entity.js.map +1 -1
  22. package/libs/tatayab-entities-library/src/entities/order-edit-item.entity.ts +101 -0
  23. package/libs/tatayab-entities-library/src/entities/order-edit-session.entity.ts +47 -0
  24. package/libs/tatayab-entities-library/src/entities/order-item-change-log.entity.ts +84 -0
  25. package/libs/tatayab-entities-library/src/entities/order-item.entity.ts +5 -0
  26. package/libs/tatayab-entities-library/src/entities/order-payment.entity.ts +107 -0
  27. package/libs/tatayab-entities-library/src/entities/order.entity.ts +15 -0
  28. package/libs/tatayab-entities-library/src/entities/refund.entity.ts +25 -0
  29. package/package.json +1 -1
  30. package/src/app.module.ts +0 -2
  31. package/src/database/config.js +4 -4
  32. package/src/database/migrations/20260615103000-add-edit-order-feature.js +283 -0
  33. package/libs/tatayab-entities-library/src/entities.zip +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"refund.entity.js","sourceRoot":"","sources":["../../../../libs/tatayab-entities-library/src/entities/refund.entity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"refund.entity.js","sourceRoot":"","sources":["../../../../libs/tatayab-entities-library/src/entities/refund.entity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ import {
2
+ Table,
3
+ Column,
4
+ Model,
5
+ ForeignKey,
6
+ BelongsTo,
7
+ DataType,
8
+ } from 'sequelize-typescript';
9
+ import { OrderEditSession } from './order-edit-session.entity';
10
+ import { OrderItem, OrderItemType } from './order-item.entity';
11
+ import { Product } from './product.entity';
12
+ import { ProductVariant } from './product-variant.entity';
13
+ import { Bundle } from './bundle.entity';
14
+
15
+ /**
16
+ * OrderEditItem — Edit Order feature.
17
+ *
18
+ * Working-copy basket line for an OrderEditSession. Seeded 1:1 from the
19
+ * order's current OrderItems when a session is opened (sourceOrderItemId
20
+ * set), then mutated freely (add/remove/change quantity) without touching
21
+ * the live OrderItems. On confirm, diffed against OrderItems and applied —
22
+ * see OrderEditController::replaceOrderItems / reconcileStock in
23
+ * tatayab-dashboard.
24
+ *
25
+ * Mirrors OrderItem's shape closely (itemType/productId/productVariantId/
26
+ * bundleId/composedSku/quantity/unitPrice/lineTotal) so the diff is a
27
+ * straightforward field-by-field comparison. bundleSelectionsJson mirrors
28
+ * OrderItemBundleSelection rows for itemType=bundle lines.
29
+ */
30
+ @Table
31
+ export class OrderEditItem extends Model {
32
+ @ForeignKey(() => OrderEditSession)
33
+ @Column({ allowNull: false })
34
+ declare sessionId: number;
35
+
36
+ @Column({
37
+ type: DataType.ENUM(...Object.values(OrderItemType)),
38
+ allowNull: false,
39
+ defaultValue: OrderItemType.PRODUCT,
40
+ })
41
+ declare itemType: OrderItemType;
42
+
43
+ // ── Product fields ────────────────────────────────────────────────────────────
44
+ @ForeignKey(() => Product)
45
+ @Column({ allowNull: true })
46
+ declare productId?: number;
47
+
48
+ @ForeignKey(() => ProductVariant)
49
+ @Column({ allowNull: true })
50
+ declare productVariantId?: number;
51
+
52
+ // ── Bundle fields ─────────────────────────────────────────────────────────────
53
+ @ForeignKey(() => Bundle)
54
+ @Column({ allowNull: true })
55
+ declare bundleId?: number;
56
+
57
+ @Column({ allowNull: true })
58
+ declare composedSku?: string;
59
+
60
+ // ── Pricing ───────────────────────────────────────────────────────────────────
61
+ @Column({ allowNull: false, defaultValue: 1 })
62
+ declare quantity: number;
63
+
64
+ @Column({ type: DataType.DECIMAL(10, 3), allowNull: false })
65
+ declare unitPrice: number;
66
+
67
+ @Column({ type: DataType.DECIMAL(10, 3), allowNull: false })
68
+ declare lineTotal: number;
69
+
70
+ // ── Diff anchor ───────────────────────────────────────────────────────────────
71
+ // Set when this line corresponds to an existing OrderItem (possibly with a
72
+ // changed quantity). Null if this line was newly added during this edit.
73
+ // On confirm: present -> update that OrderItem in place (preserving id);
74
+ // absent -> create a new OrderItem.
75
+ @ForeignKey(() => OrderItem)
76
+ @Column({ allowNull: true })
77
+ declare sourceOrderItemId?: number;
78
+
79
+ // Array of {productId, productVariantId, productNameEn, productNameAr,
80
+ // variantLabelEn, variantLabelAr, sortOrder} — same shape as
81
+ // OrderItemBundleSelection rows. Only set for itemType=bundle.
82
+ @Column({ type: DataType.JSON, allowNull: true })
83
+ declare bundleSelectionsJson?: object;
84
+
85
+ // ── Associations ──────────────────────────────────────────────────────────────
86
+
87
+ @BelongsTo(() => OrderEditSession)
88
+ declare session: OrderEditSession;
89
+
90
+ @BelongsTo(() => OrderItem, { foreignKey: 'sourceOrderItemId' })
91
+ declare sourceOrderItem?: OrderItem;
92
+
93
+ @BelongsTo(() => Product)
94
+ declare product?: Product;
95
+
96
+ @BelongsTo(() => ProductVariant)
97
+ declare variant?: ProductVariant;
98
+
99
+ @BelongsTo(() => Bundle)
100
+ declare bundle?: Bundle;
101
+ }
@@ -0,0 +1,47 @@
1
+ import {
2
+ Table,
3
+ Column,
4
+ Model,
5
+ ForeignKey,
6
+ BelongsTo,
7
+ HasMany,
8
+ DataType,
9
+ } from 'sequelize-typescript';
10
+ import { Order } from './order.entity';
11
+ import { OrderEditItem } from './order-edit-item.entity';
12
+
13
+ /**
14
+ * OrderEditSession — Edit Order feature.
15
+ *
16
+ * An in-progress edit lock on an Order. One active (non-expired) session
17
+ * per order — a second admin opening "Edit Order" while one exists sees a
18
+ * "currently being edited by X" warning with a takeover option. Cancel =
19
+ * delete this row (cascades to its OrderEditItems) — zero impact on the
20
+ * live order. Confirm = applies the edit in a transaction (see
21
+ * OrderEditController::confirm in tatayab-dashboard) then deletes this row.
22
+ *
23
+ * expiresAt = createdAt + 2 hours (SESSION_LIFETIME_MINUTES in
24
+ * OrderEditController). Expired sessions are treated as gone and silently
25
+ * replaced on the next "Edit Order" click.
26
+ */
27
+ @Table
28
+ export class OrderEditSession extends Model {
29
+ @ForeignKey(() => Order)
30
+ @Column({ allowNull: false })
31
+ declare orderId: number;
32
+
33
+ // e.g. "admin:42" — the admin who opened this session.
34
+ @Column({ allowNull: false })
35
+ declare createdBy: string;
36
+
37
+ @Column({ type: DataType.DATE, allowNull: false })
38
+ declare expiresAt: Date;
39
+
40
+ // ── Associations ──────────────────────────────────────────────────────────────
41
+
42
+ @BelongsTo(() => Order)
43
+ declare order: Order;
44
+
45
+ @HasMany(() => OrderEditItem)
46
+ declare items: OrderEditItem[];
47
+ }
@@ -0,0 +1,84 @@
1
+ import {
2
+ Table,
3
+ Column,
4
+ Model,
5
+ ForeignKey,
6
+ BelongsTo,
7
+ HasOne,
8
+ DataType,
9
+ } from 'sequelize-typescript';
10
+ import { Order } from './order.entity';
11
+ import { Refund } from './refund.entity';
12
+ import { OrderPayment } from './order-payment.entity';
13
+
14
+ export enum OrderEditScenario {
15
+ SAME = 'same',
16
+ REFUND = 'refund',
17
+ ADDITIONAL_PAYMENT = 'additional_payment',
18
+ }
19
+
20
+ /**
21
+ * OrderItemChangeLog — Edit Order feature, rule 2 (full historical
22
+ * basket-change log).
23
+ *
24
+ * One row PER CONFIRMED EDIT (not per item) — editNumber is sequential per
25
+ * order (1, 2, 3...). beforeItemsJson/afterItemsJson are full snapshots of
26
+ * the basket immediately before/after this edit, so the "Edit History" tab
27
+ * on Order Detail never needs to re-resolve product names for old entries
28
+ * even if a product is later renamed or deleted.
29
+ *
30
+ * priceDelta = newTotal - oldTotal (positive = customer owes more, negative
31
+ * = refund issued, ~zero = no payment change). scenario determines which of
32
+ * refund/additionalPayment (below) is populated, if either.
33
+ */
34
+ @Table
35
+ export class OrderItemChangeLog extends Model {
36
+ @ForeignKey(() => Order)
37
+ @Column({ allowNull: false })
38
+ declare orderId: number;
39
+
40
+ // Sequential per order: 1, 2, 3... — the Nth confirmed edit on this order.
41
+ @Column({ allowNull: false })
42
+ declare editNumber: number;
43
+
44
+ // Snapshot of the basket immediately BEFORE this edit was applied.
45
+ @Column({ type: DataType.JSON, allowNull: false })
46
+ declare beforeItemsJson: object;
47
+
48
+ // Snapshot of the basket immediately AFTER this edit was applied.
49
+ @Column({ type: DataType.JSON, allowNull: false })
50
+ declare afterItemsJson: object;
51
+
52
+ // Pre-rendered human-readable diff, e.g.
53
+ // "Invictus: 2 -> 1, Champion: 0 -> 1 (+1)". Computed once at confirm-time.
54
+ @Column({ type: DataType.TEXT, allowNull: false })
55
+ declare summary: string;
56
+
57
+ @Column({ type: DataType.DECIMAL(10, 3), allowNull: false, defaultValue: 0 })
58
+ declare priceDelta: number;
59
+
60
+ @Column({
61
+ type: DataType.ENUM(...Object.values(OrderEditScenario)),
62
+ allowNull: false,
63
+ })
64
+ declare scenario: OrderEditScenario;
65
+
66
+ // e.g. "admin:42"
67
+ @Column({ allowNull: false })
68
+ declare changedBy: string;
69
+
70
+ // ── Associations ──────────────────────────────────────────────────────────────
71
+
72
+ @BelongsTo(() => Order)
73
+ declare order: Order;
74
+
75
+ // Set only when scenario = 'refund' (and not a COD adjustment) — the
76
+ // refund this edit produced.
77
+ @HasOne(() => Refund, { foreignKey: 'editLogId' })
78
+ declare refund?: Refund;
79
+
80
+ // Set only when scenario = 'additional_payment' (and not a COD
81
+ // adjustment) — the OrderPayments top-up this edit produced.
82
+ @HasOne(() => OrderPayment, { foreignKey: 'editLogId' })
83
+ declare additionalPayment?: OrderPayment;
84
+ }
@@ -110,6 +110,11 @@ export class OrderItem extends Model {
110
110
  })
111
111
  declare fulfillmentStatus: OrderItemFulfillmentStatus;
112
112
 
113
+ // How many units have been physically received from vendor POs so far.
114
+ // When fulfilledQuantity >= quantity, the item is fully sourced.
115
+ @Column({ defaultValue: 0 })
116
+ declare fulfilledQuantity: number;
117
+
113
118
  // ── Associations ──────────────────────────────────────────────────────────────
114
119
 
115
120
  @BelongsTo(() => Order)
@@ -0,0 +1,107 @@
1
+ import {
2
+ Table,
3
+ Column,
4
+ Model,
5
+ ForeignKey,
6
+ BelongsTo,
7
+ DataType,
8
+ } from 'sequelize-typescript';
9
+ import { Order } from './order.entity';
10
+ import { OrderItemChangeLog } from './order-item-change-log.entity';
11
+
12
+ export enum OrderPaymentType {
13
+ ONLINE = 'online',
14
+ OFFLINE = 'offline',
15
+ }
16
+
17
+ export enum OrderPaymentStatus {
18
+ PENDING = 'pending',
19
+ PAID = 'paid',
20
+ FAILED = 'failed',
21
+ CANCELLED = 'cancelled',
22
+ }
23
+
24
+ /**
25
+ * OrderPayment — Edit Order feature, rule 3c (additional payments beyond
26
+ * the original).
27
+ *
28
+ * The ORIGINAL payment stays in Orders.paymentMethodSlug/paymentStatus/
29
+ * paymentReference/paidAt exactly as today — implicitly "sequence 0" and
30
+ * NEVER has a row here. Each edit that raises the order total and requires
31
+ * an extra payment gets its own row with sequence >= 1, in order
32
+ * (UNIQUE(orderId, sequence)). A never-edited order has zero rows.
33
+ *
34
+ * Multiple edits over an order's lifetime each get their own sequence —
35
+ * Order Detail's Payment tab renders the original payment plus
36
+ * order.additionalPayments[] (this table, ordered by sequence), so "two (or
37
+ * N) payment methods on one order" scales naturally.
38
+ *
39
+ * sequence is also used as:
40
+ * - the gateway-facing reference suffix "-P{sequence}" (each top-up is a
41
+ * distinct transaction on the gateway's side — see
42
+ * *PaymentService::buildOrderInput()'s $sequence param)
43
+ * - the trailing /{sequence} path segment in the unified payment-result
44
+ * URL (AbstractPaymentGatewayService::paymentResultUrl()), so
45
+ * PaymentResultController knows which row to display/update.
46
+ *
47
+ * For an offline payment method (e.g. cash), confirm() marks this row
48
+ * status='paid'/type='offline'/paidAt=now() immediately — no link is
49
+ * generated. For an online method, status starts 'pending' and a checkout
50
+ * link is generated post-commit (paymentLinkToken set on success).
51
+ */
52
+ @Table
53
+ export class OrderPayment extends Model {
54
+ @ForeignKey(() => Order)
55
+ @Column({ allowNull: false })
56
+ declare orderId: number;
57
+
58
+ // The edit that created this top-up requirement.
59
+ @ForeignKey(() => OrderItemChangeLog)
60
+ @Column({ allowNull: true })
61
+ declare editLogId?: number;
62
+
63
+ // 1, 2, 3... per order — the Nth additional payment.
64
+ @Column({ allowNull: false })
65
+ declare sequence: number;
66
+
67
+ @Column({ type: DataType.DECIMAL(10, 3), allowNull: false })
68
+ declare amount: number;
69
+
70
+ @Column({ allowNull: false })
71
+ declare currency: string;
72
+
73
+ @Column({ allowNull: false })
74
+ declare paymentMethodSlug: string;
75
+
76
+ @Column({
77
+ type: DataType.ENUM(...Object.values(OrderPaymentType)),
78
+ allowNull: false,
79
+ })
80
+ declare type: OrderPaymentType;
81
+
82
+ @Column({
83
+ type: DataType.ENUM(...Object.values(OrderPaymentStatus)),
84
+ allowNull: false,
85
+ defaultValue: OrderPaymentStatus.PENDING,
86
+ })
87
+ declare status: OrderPaymentStatus;
88
+
89
+ // Gateway transaction ID (online) or manual reference (offline).
90
+ @Column({ allowNull: true })
91
+ declare paymentReference?: string;
92
+
93
+ // Token used in the unified payment-result URL for this top-up.
94
+ @Column({ allowNull: true })
95
+ declare paymentLinkToken?: string;
96
+
97
+ @Column({ type: DataType.DATE, allowNull: true })
98
+ declare paidAt?: Date;
99
+
100
+ // ── Associations ──────────────────────────────────────────────────────────────
101
+
102
+ @BelongsTo(() => Order)
103
+ declare order: Order;
104
+
105
+ @BelongsTo(() => OrderItemChangeLog)
106
+ declare editLog?: OrderItemChangeLog;
107
+ }
@@ -14,6 +14,9 @@ import { OrderStatus } from './order-status.entity';
14
14
  import { OrderItem } from './order-item.entity';
15
15
  import { OrderStatusHistory } from './order-status-history.entity';
16
16
  import { UserTransaction } from './user-transaction.entity';
17
+ import { OrderItemChangeLog } from './order-item-change-log.entity';
18
+ import { OrderPayment } from './order-payment.entity';
19
+ import { OrderEditSession } from './order-edit-session.entity';
17
20
 
18
21
  @Table
19
22
  export class Order extends Model {
@@ -159,6 +162,9 @@ export class Order extends Model {
159
162
  })
160
163
  declare refundStatus: string;
161
164
 
165
+ @Column({ type: DataType.BOOLEAN, defaultValue: false })
166
+ declare hasEverNeededVendor: boolean;
167
+
162
168
  @Column({ type: DataType.BOOLEAN, defaultValue: false })
163
169
  declare cashCollected: boolean;
164
170
 
@@ -197,4 +203,13 @@ export class Order extends Model {
197
203
 
198
204
  @HasMany(() => UserTransaction)
199
205
  declare transactions: UserTransaction[];
206
+
207
+ @HasMany(() => OrderItemChangeLog)
208
+ declare changeLogs: OrderItemChangeLog[];
209
+
210
+ @HasMany(() => OrderPayment)
211
+ declare additionalPayments: OrderPayment[];
212
+
213
+ @HasMany(() => OrderEditSession)
214
+ declare editSessions: OrderEditSession[];
200
215
  }
@@ -9,6 +9,12 @@ import {
9
9
  import { Order } from './order.entity';
10
10
  import { User } from './user.entity';
11
11
  import { SysUser } from './sys.user.entity';
12
+ import { OrderItemChangeLog } from './order-item-change-log.entity';
13
+
14
+ export enum RefundSource {
15
+ CANCELLATION = 'cancellation',
16
+ ORDER_EDIT = 'order_edit',
17
+ }
12
18
 
13
19
  export enum RefundMethod {
14
20
  WALLET = 'wallet',
@@ -55,6 +61,23 @@ export class Refund extends Model {
55
61
  @Column({ type: DataType.TEXT, allowNull: true })
56
62
  declare notes?: string;
57
63
 
64
+ // Where this refund originated. 'order_edit' refunds are created when an
65
+ // edit lowers the order total (rule 3b) and the admin chose 'wallet' or
66
+ // 'bank' as the refund method. Existing cancellation-flow refunds keep the
67
+ // default 'cancellation' — no backfill needed.
68
+ @Column({
69
+ type: DataType.ENUM(...Object.values(RefundSource)),
70
+ allowNull: false,
71
+ defaultValue: RefundSource.CANCELLATION,
72
+ })
73
+ declare source: RefundSource;
74
+
75
+ // Set when source = 'order_edit' — links this refund to the specific edit
76
+ // that produced it (OrderItemChangeLogs.id). Null for cancellation refunds.
77
+ @ForeignKey(() => OrderItemChangeLog)
78
+ @Column({ allowNull: true })
79
+ declare editLogId?: number;
80
+
58
81
  @Column({ type: DataType.DATE, allowNull: true })
59
82
  declare processedAt?: Date;
60
83
 
@@ -72,4 +95,6 @@ export class Refund extends Model {
72
95
  declare processor?: SysUser;
73
96
  @BelongsTo(() => SysUser, { foreignKey: 'createdBy', as: 'creator' })
74
97
  declare creator?: SysUser;
98
+ @BelongsTo(() => OrderItemChangeLog, { foreignKey: 'editLogId' })
99
+ declare editLog?: OrderItemChangeLog;
75
100
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tt-entities",
3
- "version": "0.0.55",
3
+ "version": "0.1.0",
4
4
  "description": "Tatayab entities library",
5
5
  "main": "dist/libs/tatayab-entities-library/index.js",
6
6
  "types": "dist/libs/tatayab-entities-library/index.d.ts",
package/src/app.module.ts CHANGED
@@ -16,8 +16,6 @@ import { getDbModels } from '@app/tatayab-entities-library';
16
16
  password: process.env.MYSQL_PASSWORD,
17
17
  port: 3306,
18
18
  models: getDbModels(),
19
- autoLoadModels: true,
20
- synchronize: true,
21
19
  logging: false,
22
20
  define: {
23
21
  timestamps: true,
@@ -5,7 +5,7 @@ module.exports = {
5
5
  host: process.env.MYSQL_HOST,
6
6
  port: process.env.MYSQL_PORT,
7
7
  username: process.env.MYSQL_USER,
8
- password: process.env.MYSQL_PASS,
8
+ password: process.env.MYSQL_PASSWORD,
9
9
  database: process.env.MYSQL_DATABASE,
10
10
  },
11
11
  LOCAL: {
@@ -13,7 +13,7 @@ module.exports = {
13
13
  host: process.env.MYSQL_HOST,
14
14
  port: process.env.MYSQL_PORT,
15
15
  username: process.env.MYSQL_USER,
16
- password: process.env.MYSQL_PASS,
16
+ password: process.env.MYSQL_PASSWORD,
17
17
  database: process.env.MYSQL_DATABASE,
18
18
  },
19
19
  STAGING: {
@@ -21,7 +21,7 @@ module.exports = {
21
21
  host: process.env.MYSQL_HOST,
22
22
  port: process.env.MYSQL_PORT,
23
23
  username: process.env.MYSQL_USER,
24
- password: process.env.MYSQL_PASS,
24
+ password: process.env.MYSQL_PASSWORD,
25
25
  database: process.env.MYSQL_DATABASE,
26
26
  },
27
27
  PRODUCTION: {
@@ -29,7 +29,7 @@ module.exports = {
29
29
  host: process.env.MYSQL_HOST,
30
30
  port: process.env.MYSQL_PORT,
31
31
  username: process.env.MYSQL_USER,
32
- password: process.env.MYSQL_PASS,
32
+ password: process.env.MYSQL_PASSWORD,
33
33
  database: process.env.MYSQL_DATABASE,
34
34
  },
35
35
  };