@shisyamo4131/air-guard-v2-schemas 2.3.7-dev.3 → 2.3.7-dev.30

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.
package/src/Employee.js CHANGED
@@ -6,7 +6,8 @@
6
6
  import FireModel from "@shisyamo4131/air-firebase-v2";
7
7
  import { defField } from "./parts/fieldDefinitions.js";
8
8
  import { defAccessor } from "./parts/accessorDefinitions.js";
9
- import { VALUES } from "./constants/employment-status.js";
9
+ import { VALUES as EMPLOYMENT_STATUS_VALUES } from "./constants/employment-status.js";
10
+ import { VALUES as BLOOD_TYPE_VALUES } from "./constants/blood-type.js";
10
11
 
11
12
  const classProps = {
12
13
  code: defField("code", { label: "従業員コード" }),
@@ -15,30 +16,48 @@ const classProps = {
15
16
  lastNameKana: defField("lastNameKana", { required: true }),
16
17
  firstNameKana: defField("firstNameKana", { required: true }),
17
18
  displayName: defField("displayName", { required: true }),
18
- title: defField("oneLine", { label: "肩書", required: true }),
19
19
  gender: defField("gender", { required: true }),
20
- dateOfBirth: defField("dateAt", { label: "生年月日", required: true }),
20
+ dateOfBirth: defField("dateOfBirth", { required: true }),
21
21
  zipcode: defField("zipcode", { required: true }),
22
22
  prefCode: defField("prefCode", { required: true }),
23
23
  city: defField("city", { required: true }),
24
24
  address: defField("address", { required: true }),
25
25
  building: defField("building"),
26
- location: defField("location", { hidden: true }), // 非表示でOK
27
- dateOfHire: defField("dateAt", { label: "入社日", required: true }),
26
+ location: defField("location", { hidden: true }),
27
+ mobile: defField("mobile", { required: true }),
28
+ email: defField("email", { required: false }),
29
+ dateOfHire: defField("dateOfHire", { required: true }),
28
30
  employmentStatus: defField("employmentStatus", { required: true }),
29
- dateOfTermination: defField("dateAt", {
30
- label: "退職日",
31
+ title: defField("title"),
32
+ dateOfTermination: defField("dateOfTermination", {
33
+ default: null,
34
+ component: {
35
+ attrs: {
36
+ required: (item) =>
37
+ item.employmentStatus === EMPLOYMENT_STATUS_VALUES.TERMINATED.value,
38
+ disabled: (item) =>
39
+ item.employmentStatus !== EMPLOYMENT_STATUS_VALUES.TERMINATED.value,
40
+ },
41
+ },
42
+ }),
43
+ reasonOfTermination: defField("reasonOfTermination", {
31
44
  component: {
32
45
  attrs: {
33
- required: (item) => item.employmentStatus === "terminated",
46
+ required: (item) =>
47
+ item.employmentStatus === EMPLOYMENT_STATUS_VALUES.TERMINATED.value,
48
+ disabled: (item) =>
49
+ item.employmentStatus !== EMPLOYMENT_STATUS_VALUES.TERMINATED.value,
34
50
  },
35
51
  },
36
52
  }),
53
+
54
+ // Foreign related fields
37
55
  isForeigner: defField("isForeigner"),
38
56
  foreignName: defField("foreignName", {
39
57
  component: {
40
58
  attrs: {
41
59
  required: (item) => item.isForeigner,
60
+ disabled: (item) => !item.isForeigner,
42
61
  },
43
62
  },
44
63
  }),
@@ -46,61 +65,151 @@ const classProps = {
46
65
  component: {
47
66
  attrs: {
48
67
  required: (item) => item.isForeigner,
68
+ disabled: (item) => !item.isForeigner,
49
69
  },
50
70
  },
51
71
  }),
52
- residenceStatus: {
53
- type: String,
54
- default: null,
55
- label: "在留資格",
56
- required: undefined,
72
+ residenceStatus: defField("residenceStatus", {
57
73
  component: {
58
- name: "air-text-field",
59
74
  attrs: {
60
75
  required: (item) => item.isForeigner,
76
+ disabled: (item) => !item.isForeigner,
61
77
  },
62
78
  },
63
- },
64
- periodOfStay: defField("dateAt", {
65
- label: "在留期間満了日",
79
+ }),
80
+ periodOfStay: defField("periodOfStay", {
66
81
  component: {
67
82
  attrs: {
68
83
  required: (item) => item.isForeigner,
84
+ disabled: (item) => !item.isForeigner,
69
85
  },
70
86
  },
71
87
  }),
72
- remarks: defField("multipleLine", { label: "備考" }),
88
+
89
+ // Security guard related fields
90
+ hasSecurityGuardRegistration: defField("check", { label: "警備員登録" }),
91
+ dateOfSecurityGuardRegistration: defField("dateAt", {
92
+ label: "警備員登録日",
93
+ default: null,
94
+ component: {
95
+ attrs: {
96
+ required: (item) => item.hasSecurityGuardRegistration,
97
+ disabled: (item) => !item.hasSecurityGuardRegistration,
98
+ },
99
+ },
100
+ }),
101
+ bloodType: defField("bloodType", {
102
+ component: {
103
+ attrs: {
104
+ required: (item) => item.hasSecurityGuardRegistration,
105
+ disabled: (item) => !item.hasSecurityGuardRegistration,
106
+ },
107
+ },
108
+ }),
109
+ emergencyContactName: defField("emergencyContactName", {
110
+ component: {
111
+ attrs: {
112
+ required: (item) => item.hasSecurityGuardRegistration,
113
+ disabled: (item) => !item.hasSecurityGuardRegistration,
114
+ },
115
+ },
116
+ }),
117
+ emergencyContactRelation: defField("emergencyContactRelation", {
118
+ component: {
119
+ attrs: {
120
+ required: (item) => item.hasSecurityGuardRegistration,
121
+ disabled: (item) => !item.hasSecurityGuardRegistration,
122
+ },
123
+ },
124
+ }),
125
+ emergencyContactRelationDetail: defField("emergencyContactRelationDetail", {
126
+ component: {
127
+ attrs: {
128
+ required: (item) => item.hasSecurityGuardRegistration,
129
+ disabled: (item) => !item.hasSecurityGuardRegistration,
130
+ },
131
+ },
132
+ }),
133
+ emergencyContactAddress: defField("emergencyContactAddress", {
134
+ component: {
135
+ attrs: {
136
+ required: (item) => item.hasSecurityGuardRegistration,
137
+ disabled: (item) => !item.hasSecurityGuardRegistration,
138
+ },
139
+ },
140
+ }),
141
+ emergencyContactPhone: defField("emergencyContactPhone", {
142
+ component: {
143
+ attrs: {
144
+ required: (item) => item.hasSecurityGuardRegistration,
145
+ disabled: (item) => !item.hasSecurityGuardRegistration,
146
+ },
147
+ },
148
+ }),
149
+ domicile: defField("domicile", {
150
+ component: {
151
+ attrs: {
152
+ required: (item) => item.hasSecurityGuardRegistration,
153
+ disabled: (item) => !item.hasSecurityGuardRegistration,
154
+ },
155
+ },
156
+ }),
157
+
158
+ remarks: defField("remarks"),
73
159
  };
74
160
 
75
161
  /*****************************************************************************
76
- * Employee Model
77
- * @props {string} code - Employee code.
78
- * @props {string} lastName - Last name.
79
- * @props {string} firstName - First name.
80
- * @props {string} lastNameKana - Last name in Kana.
81
- * @props {string} firstNameKana - First name in Kana.
82
- * @props {string} displayName - Display name.
83
- * @props {string} title - Job title.
84
- * @props {string} gender - Gender.
85
- * @props {Date} dateOfBirth - Date of birth.
86
- * @props {string} zipcode - Postal code.
87
- * @props {string} prefCode - Prefecture code.
88
- * @props {string} city - City name.
89
- * @props {string} address - Address details.
90
- * @props {string} building - Building name.
91
- * @props {object} location - Geographical location.
92
- * @props {Date} dateOfHire - Date of hire.
93
- * @props {string} employmentStatus - Employment status.
94
- * @props {Date} dateOfTermination - Date of termination.
95
- * @props {boolean} isForeigner - Is the employee a foreigner.
96
- * @props {string} foreignName - Foreign name.
97
- * @props {string} nationality - Nationality.
98
- * @props {string} residenceStatus - Residence status.
99
- * @props {Date} periodOfStay - Period of stay expiration date.
100
- * @props {string} remarks - Additional remarks.
101
- * @computed {string} fullName - Full name combining last and first names (read-only)
102
- * @computed {string} fullAddress - Full address combining prefecture, city, and address (read-only)
103
- * @computed {string} prefecture - Prefecture name derived from `prefCode` (read-only)
162
+ * @prop {string} code - Employee code.
163
+ * @prop {string} lastName - Last name.
164
+ * @prop {string} firstName - First name.
165
+ * @prop {string} lastNameKana - Last name in Kana.
166
+ * @prop {string} firstNameKana - First name in Kana.
167
+ * @prop {string} displayName - Display name.
168
+ * @prop {string} gender - Gender.
169
+ * @prop {Date} dateOfBirth - Date of birth.
170
+ * @prop {string} zipcode - Postal code.
171
+ * @prop {string} prefCode - Prefecture code.
172
+ * @prop {string} city - City name.
173
+ * @prop {string} address - Address details.
174
+ * @prop {string} building - Building name.
175
+ * @prop {object} location - Geographical location.
176
+ * @prop {string} mobile - Mobile phone number.
177
+ * @prop {string} email - Email address.
178
+ * @prop {Date} dateOfHire - Date of hire.
179
+ * @prop {string} employmentStatus - Employment status.
180
+ * @prop {string} title - Job title.
181
+ * @prop {Date} dateOfTermination - Date of termination.
182
+ * @prop {string} reasonOfTermination - Reason for termination.
183
+ * @prop {boolean} isForeigner - Is the employee a foreigner.
184
+ * @prop {string} foreignName - Foreign name.
185
+ * @prop {string} nationality - Nationality.
186
+ * @prop {string} residenceStatus - Residence status.
187
+ * @prop {Date} periodOfStay - Period of stay expiration date.
188
+ * @prop {boolean} hasSecurityGuardRegistration - Has security guard registration.
189
+ * @prop {Date} dateOfSecurityGuardRegistration - Date of security guard registration.
190
+ * @prop {string} bloodType - Blood type.
191
+ * @prop {string} emergencyContactName - Emergency contact name.
192
+ * @prop {string} emergencyContactRelation - Emergency contact relation.
193
+ * @prop {string} emergencyContactRelationDetail - Emergency contact relation detail.
194
+ * @prop {string} emergencyContactAddress - Emergency contact address.
195
+ * @prop {string} emergencyContactPhone - Emergency contact phone number.
196
+ * @prop {string} domicile - Domicile.
197
+ * @prop {string} remarks - Additional remarks.
198
+ *
199
+ * @prop {string} fullName - Full name combining last and first names (read-only)
200
+ * @prop {string} fullNameKana - Full name in Kana combining last and first names (read-only)
201
+ * @prop {string} fullAddress - Full address combining prefecture, city, and address (read-only)
202
+ * @prop {string} prefecture - Prefecture name derived from `prefCode` (read-only)
203
+ *
204
+ * @getter
205
+ * @prop {number} age - Age calculated from `dateOfBirth` (read-only)
206
+ * @prop {number} yearsOfService - Years of service calculated from `dateOfHire` (read-only)
207
+ *
208
+ * @static
209
+ * @prop {string} STATUS_ACTIVE - constant for active employment status
210
+ * @prop {string} STATUS_TERMINATED - constant for terminated employment status
211
+ *
212
+ * @function toTerminated - Change the current employee instance to terminated status.
104
213
  *****************************************************************************/
105
214
  export default class Employee extends FireModel {
106
215
  static className = "従業員";
@@ -109,6 +218,7 @@ export default class Employee extends FireModel {
109
218
  static logicalDelete = true;
110
219
  static classProps = classProps;
111
220
  static tokenFields = [
221
+ "code",
112
222
  "lastName",
113
223
  "firstName",
114
224
  "lastNameKana",
@@ -122,21 +232,80 @@ export default class Employee extends FireModel {
122
232
  { title: "名前", key: "fullName" },
123
233
  ];
124
234
 
125
- static STATUS_ACTIVE = VALUES.ACTIVE.value;
126
- static STATUS_TERMINATED = VALUES.TERMINATED.value;
235
+ static STATUS_ACTIVE = EMPLOYMENT_STATUS_VALUES.ACTIVE.value;
236
+ static STATUS_TERMINATED = EMPLOYMENT_STATUS_VALUES.TERMINATED.value;
237
+
238
+ _skipToTerminatedCheck = false;
127
239
 
128
240
  afterInitialize(item = {}) {
129
241
  super.afterInitialize(item);
130
242
  Object.defineProperties(this, {
131
243
  fullName: defAccessor("fullName"),
244
+ fullNameKana: defAccessor("fullNameKana"),
132
245
  fullAddress: defAccessor("fullAddress"),
133
246
  prefecture: defAccessor("prefecture"),
134
247
  });
135
248
  }
136
249
 
250
+ /**
251
+ * 生年月日から年齢を計算します。
252
+ * @returns {{years: number, months: number}|null} 年齢(年数と月数)。dateOfBirthが設定されていない場合はnull。
253
+ */
254
+ get age() {
255
+ if (!this.dateOfBirth) return null;
256
+ const today = new Date();
257
+
258
+ let years = today.getUTCFullYear() - this.dateOfBirth.getUTCFullYear();
259
+ let months = today.getUTCMonth() - this.dateOfBirth.getUTCMonth();
260
+ const days = today.getUTCDate() - this.dateOfBirth.getUTCDate();
261
+
262
+ if (days < 0) {
263
+ months--;
264
+ }
265
+
266
+ if (months < 0) {
267
+ years--;
268
+ months += 12;
269
+ }
270
+
271
+ return { years, months };
272
+ }
273
+
274
+ /**
275
+ * 入社日からの勤続年数を計算します。
276
+ * - 退職日が設定されている場合は、退職日までの勤続年数を計算します。
277
+ * @returns {{years: number, months: number}|null} 勤続年数(年数と月数)。dateOfHireが設定されていない場合はnull。
278
+ */
279
+ get yearsOfService() {
280
+ if (!this.dateOfHire) return null;
281
+ const today = this.dateOfTermination || new Date();
282
+
283
+ let years = today.getUTCFullYear() - this.dateOfHire.getUTCFullYear();
284
+ let months = today.getUTCMonth() - this.dateOfHire.getUTCMonth();
285
+ const days = today.getUTCDate() - this.dateOfHire.getUTCDate();
286
+
287
+ if (days < 0) {
288
+ months--;
289
+ }
290
+
291
+ if (months < 0) {
292
+ years--;
293
+ months += 12;
294
+ }
295
+
296
+ return { years, months };
297
+ }
298
+
137
299
  /**
138
300
  * 外国籍の場合の必須フィールドを検証します。
139
- * エラーがある場合は例外をスローします。
301
+ * - エラーがある場合は例外をスローします。
302
+ * - `isForeigner` が false の場合、以下のプロパティを初期化します。
303
+ * - `foreignName`
304
+ * - `nationality`
305
+ * - `residenceStatus`
306
+ * - `periodOfStay`
307
+ * @returns {void}
308
+ * @throws {Error} 外国籍の場合に必須フィールドが未入力の場合。
140
309
  */
141
310
  _validateForeignerRequiredFields() {
142
311
  if (this.isForeigner) {
@@ -160,20 +329,84 @@ export default class Employee extends FireModel {
160
329
  "[Employee.js] periodOfStay is required when isForeigner is true."
161
330
  );
162
331
  }
332
+ } else {
333
+ // 外国籍でない場合、関連フィールドを初期化
334
+ this.foreignName = null;
335
+ this.nationality = null;
336
+ this.residenceStatus = null;
337
+ this.periodOfStay = null;
163
338
  }
164
339
  }
165
340
 
166
341
  /**
167
342
  * 退職済である場合の必須フィールドを検証します。
168
- * エラーがある場合は例外をスローします。
343
+ * - エラーがある場合は例外をスローします。
344
+ * - `employmentStatus` が `terminated` の場合、以下のプロパティを必須とします。
345
+ * - `dateOfTermination`
346
+ * - `reasonOfTermination`
347
+ * - `employmentStatus` が `active` の場合、`dateOfTermination`, `reasonOfTermination` を初期化します。
348
+ * @returns {void}
349
+ * @throws {Error} 退職済の場合に必須フィールドが未入力の場合。
169
350
  */
170
351
  _validateTerminatedRequiredFields() {
171
- if (this.employmentStatus === "terminated") {
352
+ if (this.employmentStatus === EMPLOYMENT_STATUS_VALUES.TERMINATED.value) {
172
353
  if (!this.dateOfTermination) {
173
354
  throw new Error(
174
355
  "[Employee.js] dateOfTermination is required when employmentStatus is 'terminated'."
175
356
  );
176
357
  }
358
+ if (!this.reasonOfTermination) {
359
+ throw new Error(
360
+ "[Employee.js] reasonOfTermination is required when employmentStatus is 'terminated'."
361
+ );
362
+ }
363
+ } else {
364
+ this.dateOfTermination = null;
365
+ this.reasonOfTermination = null;
366
+ }
367
+ }
368
+
369
+ _validateSecurityGuardFields() {
370
+ if (this.hasSecurityGuardRegistration) {
371
+ if (!this.dateOfSecurityGuardRegistration) {
372
+ throw new Error(
373
+ "[Employee.js] dateOfSecurityGuardRegistration is required when hasSecurityGuardRegistration is true."
374
+ );
375
+ }
376
+ if (!this.emergencyContactName) {
377
+ throw new Error(
378
+ "[Employee.js] emergencyContactName is required when hasSecurityGuardRegistration is true."
379
+ );
380
+ }
381
+ if (!this.emergencyContactRelationDetail) {
382
+ throw new Error(
383
+ "[Employee.js] emergencyContactRelationDetail is required when hasSecurityGuardRegistration is true."
384
+ );
385
+ }
386
+ if (!this.emergencyContactAddress) {
387
+ throw new Error(
388
+ "[Employee.js] emergencyContactAddress is required when hasSecurityGuardRegistration is true."
389
+ );
390
+ }
391
+ if (!this.emergencyContactPhone) {
392
+ throw new Error(
393
+ "[Employee.js] emergencyContactPhone is required when hasSecurityGuardRegistration is true."
394
+ );
395
+ }
396
+ if (!this.domicile) {
397
+ throw new Error(
398
+ "[Employee.js] domicile is required when hasSecurityGuardRegistration is true."
399
+ );
400
+ }
401
+ } else {
402
+ this.dateOfSecurityGuardRegistration = null;
403
+ this.bloodType = BLOOD_TYPE_VALUES.A.value;
404
+ this.emergencyContactName = null;
405
+ this.emergencyContactRelation = null;
406
+ this.emergencyContactRelationDetail = null;
407
+ this.emergencyContactAddress = null;
408
+ this.emergencyContactPhone = null;
409
+ this.domicile = null;
177
410
  }
178
411
  }
179
412
 
@@ -181,21 +414,98 @@ export default class Employee extends FireModel {
181
414
  * 新しい従業員ドキュメントが作成される前に実行されるフック。
182
415
  * - 親クラスの `beforeCreate` を呼び出します。
183
416
  * - 従業員が外国人の場合、外国人名と国籍が未入力であればエラーをスローします。
417
+ * @param {Object} args - Creation options.
418
+ * @param {string} [args.docId] - Document ID to use (optional).
419
+ * @param {boolean} [args.useAutonumber=true] - Whether to use auto-numbering.
420
+ * @param {Object} [args.transaction] - Firestore transaction.
421
+ * @param {Function} [args.callBack] - Callback function.
422
+ * @param {string} [args.prefix] - Path prefix.
184
423
  */
185
- async beforeCreate() {
186
- await super.beforeCreate();
424
+ async beforeCreate(args = {}) {
425
+ await super.beforeCreate(args);
187
426
  this._validateForeignerRequiredFields();
188
427
  this._validateTerminatedRequiredFields();
428
+ this._validateSecurityGuardFields();
189
429
  }
190
430
 
191
431
  /**
192
432
  * 従業員ドキュメントが更新される前に実行されるフック。
193
433
  * - 親クラスの `beforeUpdate` を呼び出します。
194
434
  * - 従業員が外国人の場合、外国人名と国籍が未入力であればエラーをスローします。
435
+ * @param {Object} args - Creation options.
436
+ * @param {Object} [args.transaction] - Firestore transaction.
437
+ * @param {Function} [args.callBack] - Callback function.
438
+ * @param {string} [args.prefix] - Path prefix.
195
439
  */
196
- async beforeUpdate() {
197
- await super.beforeUpdate();
440
+ async beforeUpdate(args = {}) {
441
+ await super.beforeUpdate(args);
442
+
443
+ // `employmentStatus` の `terminated` への直接変更の禁止
444
+ // - 従業員を退職させる場合、様々なチェックが必要になることが想定されるため、専用メソッドとして `toTerminated` を使用する。
445
+ // - `employmentStatus` を `terminated` に変更する場合は、必ず `toTerminated` メソッドを使用すること。
446
+ // - 一度退職処理した従業員の復帰処理は現状想定していないが、将来的に必要になった場合は `toActive` メソッド等を追加実装すること。
447
+ if (
448
+ !this._skipToTerminatedCheck &&
449
+ this.employmentStatus === Employee.STATUS_TERMINATED &&
450
+ this._beforeData.employmentStatus === Employee.STATUS_ACTIVE
451
+ ) {
452
+ throw new Error(
453
+ "[Employee.js] Direct changes to employmentStatus to 'terminated' are not allowed. Use toTerminated() method instead."
454
+ );
455
+ }
456
+
198
457
  this._validateForeignerRequiredFields();
199
458
  this._validateTerminatedRequiredFields();
459
+ this._validateSecurityGuardFields();
460
+ }
461
+
462
+ /**
463
+ * 現在インスタンスに読み込まれている従業員を退職状態に変更します。
464
+ * @param {Date} dateOfTermination - 退職日(Dateオブジェクト)
465
+ * @param {string} reasonOfTermination - 退職理由
466
+ * @param {Object} options - パラメータオブジェクト
467
+ * @param {Function|null} [options.transaction=null] - Firestore トランザクション関数
468
+ * @param {Function|null} [options.callBack=null] - カスタム処理用コールバック
469
+ * @param {string|null} [options.prefix=null] - パスのプレフィックス
470
+ * @returns {Promise<DocumentReference>} 更新されたドキュメントの参照
471
+ * @throws {Error} docIdが存在しない場合、または有効なdateOfTerminationが提供されていない場合。
472
+ */
473
+ async toTerminated(dateOfTermination, reasonOfTermination, options = {}) {
474
+ if (!this.docId) {
475
+ throw new Error(
476
+ "[Employee.js] docId is required to terminate an employee."
477
+ );
478
+ }
479
+ if (!dateOfTermination || !(dateOfTermination instanceof Date)) {
480
+ throw new Error(
481
+ "[Employee.js] A valid dateOfTermination is required to terminate an employee."
482
+ );
483
+ }
484
+ if (dateOfTermination < this.dateOfHire) {
485
+ throw new Error(
486
+ "[Employee.js] dateOfTermination cannot be earlier than dateOfHire."
487
+ );
488
+ }
489
+
490
+ if (!reasonOfTermination || typeof reasonOfTermination !== "string") {
491
+ throw new Error(
492
+ "[Employee.js] A valid reasonOfTermination is required to terminate an employee."
493
+ );
494
+ }
495
+
496
+ this.employmentStatus = Employee.STATUS_TERMINATED;
497
+ this.dateOfTermination = dateOfTermination;
498
+ this.reasonOfTermination = reasonOfTermination;
499
+
500
+ this._skipToTerminatedCheck = true;
501
+
502
+ try {
503
+ return await this.update(options);
504
+ } catch (error) {
505
+ this.rollback();
506
+ throw error;
507
+ } finally {
508
+ this._skipToTerminatedCheck = false;
509
+ }
200
510
  }
201
511
  }
@@ -181,7 +181,6 @@
181
181
  * @method delete - Override delete method to allow deletion even when isLocked is true
182
182
  *****************************************************************************/
183
183
  import OperationResult from "./OperationResult.js";
184
- import Operation from "./Operation.js";
185
184
 
186
185
  export default class OperationBilling extends OperationResult {
187
186
  static className = "稼働請求";
@@ -192,28 +191,43 @@ export default class OperationBilling extends OperationResult {
192
191
  { title: "売上金額", key: "salesAmount", value: "salesAmount" },
193
192
  ];
194
193
 
195
- async create() {
196
- return Promise.reject(new Error("[OperationBilling.js] Not implemented."));
194
+ /**
195
+ * Override beforeUpdate to skip `isLocked` check and sync customerId and apply agreement if key changed
196
+ * @param {Object} args - Creation options.
197
+ * @param {Object} [args.transaction] - Firestore transaction.
198
+ * @param {Function} [args.callBack] - Callback function.
199
+ * @param {string} [args.prefix] - Path prefix.
200
+ * @returns {Promise<void>}
201
+ */
202
+ async beforeUpdate(args = {}) {
203
+ await super.beforeUpdate(args);
204
+ // Sync customerId and apply agreement if key changed
205
+ if (this.key === this._beforeData.key) return;
206
+ await this._syncCustomerIdAndApplyAgreement(args);
197
207
  }
198
208
 
199
209
  /**
200
- * Override update method to allow editing even when isLocked is true
201
- * @param {*} options
202
- * @returns {Promise<void>}
210
+ * Override create method to disallow creation of OperationBilling instances
211
+ * @returns
203
212
  */
204
- async update(options = {}) {
205
- // isLockedのチェックをスキップして、親クラス(Operation)のupdateを直接呼び出す
206
- return await Operation.prototype.update.call(this, options);
213
+ async create() {
214
+ return Promise.reject(
215
+ new Error(
216
+ "[OperationBilling.js] Creation of OperationBilling is not implemented."
217
+ )
218
+ );
207
219
  }
208
220
 
209
221
  /**
210
- * Override delete method to allow deletion even when isLocked is true
211
- * @param {*} options
222
+ * Override delete method to disallow deletion of OperationBilling instances
212
223
  * @returns {Promise<void>}
213
224
  */
214
- async delete(options = {}) {
215
- // isLockedのチェックをスキップして、親クラス(Operation)のdeleteを直接呼び出す
216
- return await Operation.prototype.delete.call(this, options);
225
+ async delete() {
226
+ return Promise.reject(
227
+ new Error(
228
+ "[OperationBilling.js] Deletion of OperationBilling is not implemented."
229
+ )
230
+ );
217
231
  }
218
232
 
219
233
  /**