ojamoto_models 1.0.13 → 1.0.14

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.
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("phone_verification_otps", {
6
+ phone_verification_otp_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ user_id: {
13
+ type: Sequelize.INTEGER,
14
+ allowNull: false,
15
+ unique: true,
16
+ references: { model: "users", key: "user_id" },
17
+ onDelete: "CASCADE",
18
+ onUpdate: "CASCADE",
19
+ },
20
+ otp: {
21
+ type: Sequelize.STRING,
22
+ allowNull: false,
23
+ comment: "Hashed before storage",
24
+ },
25
+ expires_at: {
26
+ type: Sequelize.DATE,
27
+ allowNull: false,
28
+ },
29
+ verification_attempts: {
30
+ type: Sequelize.INTEGER,
31
+ allowNull: false,
32
+ defaultValue: 0,
33
+ },
34
+ created_at: {
35
+ type: Sequelize.DATE,
36
+ allowNull: false,
37
+ },
38
+ updated_at: {
39
+ type: Sequelize.DATE,
40
+ allowNull: false,
41
+ },
42
+ });
43
+ },
44
+ async down(queryInterface) {
45
+ await queryInterface.dropTable("phone_verification_otps");
46
+ },
47
+ };
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("banks", {
6
+ bank_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ provider_bank_id: {
13
+ type: Sequelize.INTEGER,
14
+ allowNull: false,
15
+ unique: true,
16
+ comment: "Flutterwave's own bank ID",
17
+ },
18
+ bank_name: {
19
+ type: Sequelize.STRING(150),
20
+ allowNull: false,
21
+ },
22
+ bank_code: {
23
+ type: Sequelize.STRING(20),
24
+ allowNull: false,
25
+ unique: true,
26
+ },
27
+ country: {
28
+ type: Sequelize.STRING(2),
29
+ allowNull: false,
30
+ defaultValue: "NG",
31
+ },
32
+ is_active: {
33
+ type: Sequelize.BOOLEAN,
34
+ allowNull: false,
35
+ defaultValue: true,
36
+ },
37
+ created_at: {
38
+ type: Sequelize.DATE,
39
+ allowNull: false,
40
+ },
41
+ updated_at: {
42
+ type: Sequelize.DATE,
43
+ allowNull: false,
44
+ },
45
+ });
46
+ },
47
+
48
+ async down(queryInterface) {
49
+ await queryInterface.dropTable("banks");
50
+ },
51
+ };
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("user_bank_accounts", {
6
+ user_bank_account_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ user_id: {
13
+ type: Sequelize.INTEGER,
14
+ allowNull: false,
15
+ unique: true,
16
+ references: { model: "users", key: "user_id" },
17
+ onDelete: "CASCADE",
18
+ onUpdate: "CASCADE",
19
+ comment: "One verified payout account per user",
20
+ },
21
+ bank_id: {
22
+ type: Sequelize.INTEGER,
23
+ allowNull: false,
24
+ references: { model: "banks", key: "bank_id" },
25
+ onDelete: "RESTRICT",
26
+ onUpdate: "CASCADE",
27
+ },
28
+ account_number: {
29
+ type: Sequelize.STRING(20),
30
+ allowNull: false,
31
+ },
32
+ account_name: {
33
+ type: Sequelize.STRING(150),
34
+ allowNull: false,
35
+ comment: "Name returned by the resolve API — used for the match check",
36
+ },
37
+ verified_at: {
38
+ type: Sequelize.DATE,
39
+ allowNull: false,
40
+ },
41
+ created_at: {
42
+ type: Sequelize.DATE,
43
+ allowNull: false,
44
+ },
45
+ updated_at: {
46
+ type: Sequelize.DATE,
47
+ allowNull: false,
48
+ },
49
+ });
50
+ },
51
+ async down(queryInterface) {
52
+ await queryInterface.dropTable("user_bank_accounts");
53
+ },
54
+ };
package/models/Bank ADDED
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ const { Model, DataTypes } = require("sequelize");
3
+
4
+ module.exports = (sequelize) => {
5
+ class Bank extends Model {
6
+ static associate() {}
7
+ }
8
+
9
+ Bank.init(
10
+ {
11
+ bank_id: {
12
+ type: DataTypes.INTEGER,
13
+ primaryKey: true,
14
+ autoIncrement: true,
15
+ },
16
+ provider_bank_id: {
17
+ type: DataTypes.INTEGER,
18
+ allowNull: false,
19
+ unique: true,
20
+ },
21
+ bank_name: {
22
+ type: DataTypes.STRING(150),
23
+ allowNull: false,
24
+ },
25
+ bank_code: {
26
+ type: DataTypes.STRING(20),
27
+ allowNull: false,
28
+ unique: true,
29
+ },
30
+ country: {
31
+ type: DataTypes.STRING(2),
32
+ allowNull: false,
33
+ defaultValue: "NG",
34
+ },
35
+ is_active: {
36
+ type: DataTypes.BOOLEAN,
37
+ allowNull: false,
38
+ defaultValue: true,
39
+ },
40
+ },
41
+ {
42
+ sequelize,
43
+ modelName: "Bank",
44
+ tableName: "banks",
45
+ timestamps: true,
46
+ createdAt: "created_at",
47
+ updatedAt: "updated_at",
48
+ },
49
+ );
50
+
51
+ Bank.removeAttribute("id");
52
+ return Bank;
53
+ };
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ const { Model, DataTypes } = require("sequelize");
3
+
4
+ module.exports = (sequelize) => {
5
+ class PhoneVerificationOtp extends Model {
6
+ static associate(models) {
7
+ PhoneVerificationOtp.belongsTo(models.User, {
8
+ foreignKey: "user_id",
9
+ as: "user",
10
+ });
11
+ }
12
+ }
13
+
14
+ PhoneVerificationOtp.init(
15
+ {
16
+ phone_verification_otp_id: {
17
+ type: DataTypes.INTEGER,
18
+ primaryKey: true,
19
+ autoIncrement: true,
20
+ },
21
+ user_id: {
22
+ type: DataTypes.INTEGER,
23
+ allowNull: false,
24
+ unique: true,
25
+ },
26
+ otp: {
27
+ type: DataTypes.STRING,
28
+ allowNull: false,
29
+ },
30
+ expires_at: {
31
+ type: DataTypes.DATE,
32
+ allowNull: false,
33
+ },
34
+ verification_attempts: {
35
+ type: DataTypes.INTEGER,
36
+ allowNull: false,
37
+ defaultValue: 0,
38
+ },
39
+ },
40
+ {
41
+ sequelize,
42
+ modelName: "PhoneVerificationOtp",
43
+ tableName: "phone_verification_otps",
44
+ timestamps: true,
45
+ createdAt: "created_at",
46
+ updatedAt: "updated_at",
47
+ },
48
+ );
49
+
50
+ PhoneVerificationOtp.removeAttribute("id");
51
+ return PhoneVerificationOtp;
52
+ };
@@ -19,6 +19,11 @@ module.exports = (sequelize) => {
19
19
  foreignKey: "seller_profile_id",
20
20
  as: "badges",
21
21
  });
22
+
23
+ SellerProfile.hasOne(models.SellerSubscription, {
24
+ foreignKey: "seller_profile_id",
25
+ as: "subscription",
26
+ });
22
27
  }
23
28
  }
24
29
 
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ const { Model, DataTypes } = require("sequelize");
3
+ const {
4
+ SUBSCRIPTION_BILLING_TYPE,
5
+ SUBSCRIPTION_STATUS,
6
+ } = require("../services/constants");
7
+
8
+ module.exports = (sequelize) => {
9
+ class SellerSubscription extends Model {
10
+ static associate(models) {
11
+ SellerSubscription.belongsTo(models.SellerProfile, {
12
+ foreignKey: "seller_profile_id",
13
+ as: "sellerProfile",
14
+ });
15
+ }
16
+ }
17
+
18
+ SellerSubscription.init(
19
+ {
20
+ seller_subscription_id: {
21
+ type: DataTypes.INTEGER,
22
+ primaryKey: true,
23
+ autoIncrement: true,
24
+ },
25
+ seller_profile_id: {
26
+ type: DataTypes.INTEGER,
27
+ allowNull: false,
28
+ references: { model: "seller_profiles", key: "seller_profile_id" },
29
+ onDelete: "CASCADE",
30
+ },
31
+ tier: {
32
+ type: DataTypes.STRING(20),
33
+ allowNull: false,
34
+ },
35
+ billing_type: {
36
+ type: DataTypes.ENUM(...Object.values(SUBSCRIPTION_BILLING_TYPE)),
37
+ allowNull: false,
38
+ },
39
+ status: {
40
+ type: DataTypes.ENUM(...Object.values(SUBSCRIPTION_STATUS)),
41
+ allowNull: false,
42
+ defaultValue: SUBSCRIPTION_STATUS.ACTIVE,
43
+ },
44
+ amount_paid: {
45
+ type: DataTypes.DECIMAL(15, 2),
46
+ allowNull: false,
47
+ },
48
+ next_billing_date: {
49
+ type: DataTypes.DATE,
50
+ allowNull: true,
51
+ comment: "NULL for ONE_TIME — never bills again",
52
+ },
53
+ past_due_since: {
54
+ type: DataTypes.DATE,
55
+ allowNull: true,
56
+ },
57
+ },
58
+ {
59
+ sequelize,
60
+ modelName: "SellerSubscription",
61
+ tableName: "seller_subscriptions",
62
+ timestamps: true,
63
+ createdAt: "created_at",
64
+ updatedAt: "updated_at",
65
+ },
66
+ );
67
+
68
+ SellerSubscription.removeAttribute("id");
69
+ return SellerSubscription;
70
+ };
package/models/User.js CHANGED
@@ -109,6 +109,12 @@ module.exports = (sequelize) => {
109
109
  foreignKey: "user_id",
110
110
  as: "wishlist",
111
111
  });
112
+
113
+ // Bank
114
+ User.hasOne(models.UserBankAccount, {
115
+ foreignKey: "user_id",
116
+ as: "bankAccount",
117
+ });
112
118
  }
113
119
  }
114
120
 
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ const { Model, DataTypes } = require("sequelize");
3
+
4
+ module.exports = (sequelize) => {
5
+ class UserBankAccount extends Model {
6
+ static associate(models) {
7
+ UserBankAccount.belongsTo(models.User, {
8
+ foreignKey: "user_id",
9
+ as: "user",
10
+ });
11
+ UserBankAccount.belongsTo(models.Bank, {
12
+ foreignKey: "bank_id",
13
+ as: "bank",
14
+ });
15
+ }
16
+ }
17
+
18
+ UserBankAccount.init(
19
+ {
20
+ user_bank_account_id: {
21
+ type: DataTypes.INTEGER,
22
+ primaryKey: true,
23
+ autoIncrement: true,
24
+ },
25
+ user_id: {
26
+ type: DataTypes.INTEGER,
27
+ allowNull: false,
28
+ unique: true,
29
+ },
30
+ bank_id: {
31
+ type: DataTypes.INTEGER,
32
+ allowNull: false,
33
+ },
34
+ account_number: {
35
+ type: DataTypes.STRING(20),
36
+ allowNull: false,
37
+ },
38
+ account_name: {
39
+ type: DataTypes.STRING(150),
40
+ allowNull: false,
41
+ },
42
+ verified_at: {
43
+ type: DataTypes.DATE,
44
+ allowNull: false,
45
+ },
46
+ },
47
+ {
48
+ sequelize,
49
+ modelName: "UserBankAccount",
50
+ tableName: "user_bank_accounts",
51
+ timestamps: true,
52
+ createdAt: "created_at",
53
+ updatedAt: "updated_at",
54
+ },
55
+ );
56
+
57
+ return UserBankAccount;
58
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ojamoto_models",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "migrate:up": "npx sequelize-cli db:migrate",
@@ -1,14 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  module.exports = {
4
- // - Generic status IDs (legacy support) ──────────────────────────────────
5
- STATUS_ID: {
6
- ACTIVE: 1,
7
- INACTIVE: 2,
8
- DELETED: 3,
9
- BLOCKED: 4,
10
- },
11
-
12
4
  // - users.status ─────────────────────────────────────────────────────────
13
5
  USER_STATUS: {
14
6
  ACTIVE: "ACTIVE",
@@ -22,6 +14,14 @@ module.exports = {
22
14
  SELLER: "SELLER",
23
15
  },
24
16
 
17
+ // - addresses.label ───────────────────────────────────────────────────────────
18
+ ADDRESS_LABEL: {
19
+ HOME: "HOME",
20
+ BUSINESS: "BUSINESS",
21
+ PICKUP: "PICKUP",
22
+ OTHER: "OTHER",
23
+ },
24
+
25
25
  // - seller_profiles.user_type ───────────────────────────────────────────────────────────
26
26
  SELLER_TYPE: {
27
27
  INDIVIDUAL: "INDIVIDUAL",
@@ -36,27 +36,44 @@ module.exports = {
36
36
  PREMIUM_SELLER: "PREMIUM_SELLER",
37
37
  },
38
38
 
39
- // - addresses.label ───────────────────────────────────────────────────────────
40
- ADDRESS_LABEL: {
41
- HOME: "HOME",
42
- BUSINESS: "BUSINESS",
43
- PICKUP: "PICKUP",
44
- OTHER: "OTHER",
39
+ // - seller_profiles.verification_status ──────────────────────────────────
40
+ SELLER_VERIFICATION_STATUS: {
41
+ UNVERIFIED: "UNVERIFIED",
42
+ VERIFIED: "VERIFIED",
43
+ SUSPENDED: "SUSPENDED",
45
44
  },
46
45
 
47
- // - admin_users.role ─────────────────────────────────────────────────────
48
- ADMIN_ROLE: {
49
- SUPPORT_AGENT: "SUPPORT_AGENT",
50
- SENIOR_REVIEWER: "SENIOR_REVIEWER",
51
- SUPER_ADMIN: "SUPER_ADMIN",
46
+ // - seller_profiles.tier ─────────────────────────────────────────────────
47
+ SELLER_TIER: {
48
+ TIER_1: "TIER_1",
49
+ TIER_2: "TIER_2",
50
+ TIER_3: "TIER_3",
52
51
  },
53
52
 
54
- // - admin_users.status ───────────────────────────────────────────────────
55
- ADMIN_STATUS: {
53
+ SELLER_TIER_CONFIG: {
54
+ TIER_1: { fee_percentage: 0.05, listing_limit: 10, requires_kyc: false },
55
+ TIER_2: { fee_percentage: 0.045, listing_limit: 100, requires_kyc: true },
56
+ TIER_3: { fee_percentage: 0.04, listing_limit: null, requires_kyc: true },
57
+ },
58
+
59
+ SELLER_TIER_PRICING: {
60
+ TIER_2: { one_time: 50000, recurring: 5000 },
61
+ TIER_3: { one_time: 100000, recurring: 10000 },
62
+ },
63
+
64
+ SUBSCRIPTION_BILLING_TYPE: {
65
+ ONE_TIME: "ONE_TIME",
66
+ RECURRING: "RECURRING",
67
+ },
68
+
69
+ SUBSCRIPTION_STATUS: {
56
70
  ACTIVE: "ACTIVE",
57
- SUSPENDED: "SUSPENDED",
71
+ PAST_DUE: "PAST_DUE",
72
+ CANCELLED: "CANCELLED",
58
73
  },
59
74
 
75
+ RECURRING_GRACE_PERIOD_DAYS: 3,
76
+
60
77
  // - kyc_verifications.verification_status ────────────────────────────────
61
78
  KYC_STATUS: {
62
79
  PENDING: "PENDING",
@@ -71,34 +88,17 @@ module.exports = {
71
88
  QOREID: "QOREID",
72
89
  },
73
90
 
74
- // - seller_profiles.verification_status ──────────────────────────────────
75
- SELLER_VERIFICATION_STATUS: {
76
- UNVERIFIED: "UNVERIFIED",
77
- VERIFIED: "VERIFIED",
78
- SUSPENDED: "SUSPENDED",
79
- },
80
-
81
- // Wallet funding fee - passed on to the user at top-up time 0.8%
82
- FUNDING_FEE_PERCENTAGE: 0.008,
83
-
84
- // - seller_profiles.tier ─────────────────────────────────────────────────
85
- SELLER_TIER: {
86
- TIER_1: "TIER_1",
87
- TIER_2: "TIER_2",
88
- TIER_3: "TIER_3",
89
- },
90
-
91
- SELLER_TIER_CONFIG: {
92
- TIER_1: { fee_percentage: 0.05, listing_limit: 10, requires_kyc: false },
93
- TIER_2: { fee_percentage: 0.045, listing_limit: 100, requires_kyc: true },
94
- TIER_3: { fee_percentage: 0.04, listing_limit: null, requires_kyc: true },
95
- },
96
-
97
91
  PIN_LOCK: {
98
92
  MAX_ATTEMPTS_BEFORE_LOCK: 3,
99
93
  LOCK_DURATION_HOURS: 24,
100
94
  },
101
95
 
96
+ PHONE_OTP: {
97
+ MAX_ATTEMPTS: 3,
98
+ EXPIRY_MINUTES: 5,
99
+ RESEND_COOLDOWN_SECONDS: 60,
100
+ },
101
+
102
102
  // - products.condition ───────────────────────────────────────────────────
103
103
  PRODUCT_CONDITION: {
104
104
  NEW: "NEW",
@@ -114,6 +114,61 @@ module.exports = {
114
114
  REMOVED: "REMOVED",
115
115
  },
116
116
 
117
+ PRODUCT_CATEGORY: {
118
+ ENGINE: "ENGINE",
119
+ TRANSMISSION: "TRANSMISSION",
120
+ BRAKE_SYSTEM: "BRAKE_SYSTEM",
121
+ SUSPENSION_STEERING: "SUSPENSION_STEERING",
122
+ ELECTRICAL_BATTERY: "ELECTRICAL_BATTERY",
123
+ BODY_PARTS: "BODY_PARTS",
124
+ EXHAUST_SYSTEM: "EXHAUST_SYSTEM",
125
+ COOLING_SYSTEM: "COOLING_SYSTEM",
126
+ FUEL_SYSTEM: "FUEL_SYSTEM",
127
+ WHEELS_TYRES: "WHEELS_TYRES",
128
+ INTERIOR_ACCESSORIES: "INTERIOR_ACCESSORIES",
129
+ EXTERIOR_ACCESSORIES: "EXTERIOR_ACCESSORIES",
130
+ LIGHTING: "LIGHTING",
131
+ AC_HEATING: "AC_HEATING",
132
+ FILTERS: "FILTERS",
133
+ BELTS_HOSES: "BELTS_HOSES",
134
+ TOOLS_EQUIPMENT: "TOOLS_EQUIPMENT",
135
+ OILS_FLUIDS: "OILS_FLUIDS",
136
+ OTHERS: "OTHERS",
137
+ },
138
+
139
+ PRODUCT_BRAND: {
140
+ TOYOTA: "TOYOTA",
141
+ HONDA: "HONDA",
142
+ LEXUS: "LEXUS",
143
+ NISSAN: "NISSAN",
144
+ MERCEDES_BENZ: "MERCEDES_BENZ",
145
+ BMW: "BMW",
146
+ FORD: "FORD",
147
+ HYUNDAI: "HYUNDAI",
148
+ KIA: "KIA",
149
+ MAZDA: "MAZDA",
150
+ MITSUBISHI: "MITSUBISHI",
151
+ VOLKSWAGEN: "VOLKSWAGEN",
152
+ PEUGEOT: "PEUGEOT",
153
+ ACURA: "ACURA",
154
+ INFINITI: "INFINITI",
155
+ CHEVROLET: "CHEVROLET",
156
+ LAND_ROVER: "LAND_ROVER",
157
+ JEEP: "JEEP",
158
+ AUDI: "AUDI",
159
+ VOLVO: "VOLVO",
160
+ SUZUKI: "SUZUKI",
161
+ MACK: "MACK",
162
+ SCANIA: "SCANIA",
163
+ MAN: "MAN",
164
+ IVECO: "IVECO",
165
+ DAF: "DAF",
166
+ HOWO: "HOWO",
167
+ BOSCH: "BOSCH",
168
+ DENSO: "DENSO",
169
+ OTHERS: "OTHERS",
170
+ },
171
+
117
172
  // - wallet_transactions.type ─────────────────────────────────────────────
118
173
  WALLET_TRANSACTION_TYPE: {
119
174
  DEPOSIT: "DEPOSIT",
@@ -131,6 +186,9 @@ module.exports = {
131
186
  EXPIRED: "EXPIRED",
132
187
  },
133
188
 
189
+ // Wallet funding fee - passed on to the user at top-up time 0.8%
190
+ FUNDING_FEE_PERCENTAGE: 0.008,
191
+
134
192
  // - escrows.status ───────────────────────────────────────────────────────
135
193
  ESCROW_STATUS: {
136
194
  PENDING: "PENDING",
@@ -179,6 +237,13 @@ module.exports = {
179
237
  DELIVERED: "DELIVERED",
180
238
  },
181
239
 
240
+ // TBD
241
+ LOGISTICS_PROVIDER: {
242
+ GIG: "GIG",
243
+ DHL: "DHL",
244
+ FEDEX: "FedEx",
245
+ },
246
+
182
247
  // - webhook_logs.status ──────────────────────────────────────────────────
183
248
  WEBHOOK_STATUS: {
184
249
  ACCEPTED: "ACCEPTED",
@@ -187,10 +252,32 @@ module.exports = {
187
252
 
188
253
  STOCK_ADJUSTMENT_TYPE: {
189
254
  RESTOCK: "RESTOCK",
255
+ SALE_ONLINE: "SALE_ONLINE",
190
256
  SALE_OFFLINE: "SALE_OFFLINE",
191
257
  DAMAGE: "DAMAGE",
192
258
  CORRECTION: "CORRECTION",
193
259
  BULK_UPDATE: "BULK_UPDATE",
194
260
  OTHER: "OTHER",
195
261
  },
262
+
263
+ // - Generic status IDs (legacy support) ──────────────────────────────────
264
+ STATUS_ID: {
265
+ ACTIVE: 1,
266
+ INACTIVE: 2,
267
+ DELETED: 3,
268
+ BLOCKED: 4,
269
+ },
270
+
271
+ // - admin_users.role ─────────────────────────────────────────────────────
272
+ ADMIN_ROLE: {
273
+ SUPPORT_AGENT: "SUPPORT_AGENT",
274
+ SENIOR_REVIEWER: "SENIOR_REVIEWER",
275
+ SUPER_ADMIN: "SUPER_ADMIN",
276
+ },
277
+
278
+ // - admin_users.status ───────────────────────────────────────────────────
279
+ ADMIN_STATUS: {
280
+ ACTIVE: "ACTIVE",
281
+ SUSPENDED: "SUSPENDED",
282
+ },
196
283
  };