@shisyamo4131/air-guard-v2-schemas 1.3.1-dev.9 → 2.0.1
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/index.js +1 -0
- package/package.json +2 -2
- package/src/ArrangementNotification.js +17 -3
- package/src/Billing.js +82 -9
- package/src/Customer.js +40 -0
- package/src/OperationBilling.js +58 -8
- package/src/OperationResult.js +105 -45
- package/src/Site.js +27 -11
- package/src/parts/fieldDefinitions.js +12 -0
package/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as Agreement } from "./src/Agreement.js";
|
|
2
2
|
export { default as ArrangementNotification } from "./src/ArrangementNotification.js";
|
|
3
|
+
export { default as Billing } from "./src/Billing.js";
|
|
3
4
|
export { default as Company } from "./src/Company.js";
|
|
4
5
|
export { default as Customer, CustomerMinimal } from "./src/Customer.js";
|
|
5
6
|
export { default as CutoffDate } from "./src/utils/CutoffDate.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shisyamo4131/air-guard-v2-schemas",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Schemas for AirGuard V2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"release:major": "npm version major && npm publish"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@shisyamo4131/air-firebase-v2": "^
|
|
42
|
+
"@shisyamo4131/air-firebase-v2": "^2.0.0"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@holiday-jp/holiday_jp": "^2.5.1"
|
|
@@ -139,7 +139,6 @@ import {
|
|
|
139
139
|
VALUES,
|
|
140
140
|
OPTIONS,
|
|
141
141
|
} from "./constants/arrangement-notification-status.js";
|
|
142
|
-
import { runTransaction } from "firebase/firestore";
|
|
143
142
|
|
|
144
143
|
const classProps = {
|
|
145
144
|
status: defField("arrangementNotificationStatus", { required: true }),
|
|
@@ -431,6 +430,14 @@ export default class ArrangementNotification extends SiteOperationScheduleDetail
|
|
|
431
430
|
arguments: { id },
|
|
432
431
|
};
|
|
433
432
|
try {
|
|
433
|
+
// サーバー側での実行を禁止
|
|
434
|
+
if (this.type === "SERVER") {
|
|
435
|
+
throw new Error(
|
|
436
|
+
"fetchDocsBySiteOperationScheduleId is not supported on server side. " +
|
|
437
|
+
"Please use this method only on client side or implement server-specific logic with explicit prefix handling."
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
434
441
|
const instance = new ArrangementNotification();
|
|
435
442
|
const constraints = [["where", "siteOperationScheduleId", "==", id]];
|
|
436
443
|
const result = await instance.fetchDocs({ constraints });
|
|
@@ -459,6 +466,14 @@ export default class ArrangementNotification extends SiteOperationScheduleDetail
|
|
|
459
466
|
arguments: { ...options, transaction },
|
|
460
467
|
};
|
|
461
468
|
try {
|
|
469
|
+
// サーバー側での実行を禁止
|
|
470
|
+
if (this.type === "SERVER") {
|
|
471
|
+
throw new Error(
|
|
472
|
+
"bulkDelete is not supported on server side. " +
|
|
473
|
+
"Please use this method only on client side or implement server-specific logic with explicit prefix handling."
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
462
477
|
const { siteOperationScheduleId, workerIds = [] } = options;
|
|
463
478
|
if (!siteOperationScheduleId) {
|
|
464
479
|
throw new Error("siteOperationScheduleId is required");
|
|
@@ -495,8 +510,7 @@ export default class ArrangementNotification extends SiteOperationScheduleDetail
|
|
|
495
510
|
if (transaction) {
|
|
496
511
|
await performTransaction(transaction);
|
|
497
512
|
} else {
|
|
498
|
-
|
|
499
|
-
await runTransaction(firestore, performTransaction);
|
|
513
|
+
await this.runTransaction(performTransaction);
|
|
500
514
|
}
|
|
501
515
|
} catch (error) {
|
|
502
516
|
throw new ContextualError(error.message, context);
|
package/src/Billing.js
CHANGED
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @prop {string} customerId - customer document id
|
|
8
8
|
* @prop {string} siteId - site document id
|
|
9
|
-
* @prop {
|
|
10
|
-
* @prop {Date}
|
|
11
|
-
* @prop {Date} paymentDueDate - payment due date
|
|
9
|
+
* @prop {Date} billingDateAt - billing date
|
|
10
|
+
* @prop {Date} paymentDueDateAt - payment due date
|
|
12
11
|
*
|
|
13
12
|
* @prop {Array} paymentRecords - payment records (not implemented yet)
|
|
14
13
|
*
|
|
@@ -17,10 +16,14 @@
|
|
|
17
16
|
* @prop {Object} adjustment - adjustment
|
|
18
17
|
* @prop {string} remarks - remarks
|
|
19
18
|
*
|
|
19
|
+
* @prop {string} billingMonth - billing month (YYYY-MM format) (read-only)
|
|
20
|
+
* @prop {Date} billingDate - billing date (YYYY-MM-DD format) (read-only)
|
|
21
|
+
* @prop {string} paymentDueMonth - payment due month (YYYY-MM format) (read-only)
|
|
22
|
+
* @prop {Date} paymentDueDate - payment due date (YYYY-MM-DD format) (read-only)
|
|
20
23
|
* @prop {number} subtotal - subtotal (excluding tax) (computed-readonly)
|
|
21
24
|
* @prop {number} taxAmount - tax amount (computed-readonly)
|
|
22
25
|
* @prop {number} totalAmount - total amount (including tax) (computed-readonly)
|
|
23
|
-
* @prop {Array<Object>}
|
|
26
|
+
* @prop {Array<Object>} summary - summary for display (computed-readonly)
|
|
24
27
|
*****************************************************************************/
|
|
25
28
|
|
|
26
29
|
import FireModel from "@shisyamo4131/air-firebase-v2";
|
|
@@ -37,9 +40,8 @@ const STATUS = {
|
|
|
37
40
|
const classProps = {
|
|
38
41
|
customerId: defField("customerId", { required: true }),
|
|
39
42
|
siteId: defField("siteId", { required: true }),
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
paymentDueDateAt: defField("date"),
|
|
43
|
+
billingDateAt: defField("dateAt", { required: true }),
|
|
44
|
+
paymentDueDateAt: defField("dateAt"),
|
|
43
45
|
|
|
44
46
|
// 入金管理用配列(現時点では未使用 将来の拡張用)
|
|
45
47
|
paymentRecords: defField("array", { default: [] }), // Not implemented yet
|
|
@@ -66,6 +68,75 @@ export default class Billing extends FireModel {
|
|
|
66
68
|
afterInitialize(item = {}) {
|
|
67
69
|
super.afterInitialize(item);
|
|
68
70
|
|
|
71
|
+
// billingDate (YYYY-MM-DD) と billingMonth (YYYY-MM) の計算用プロパティを定義
|
|
72
|
+
Object.defineProperties(this, {
|
|
73
|
+
billingDate: {
|
|
74
|
+
configurable: true,
|
|
75
|
+
enumerable: true,
|
|
76
|
+
get() {
|
|
77
|
+
if (!this.billingDateAt) return null;
|
|
78
|
+
const jstDate = new Date(
|
|
79
|
+
this.billingDateAt.getTime() + 9 * 60 * 60 * 1000
|
|
80
|
+
); /* JST補正 */
|
|
81
|
+
const year = jstDate.getUTCFullYear();
|
|
82
|
+
const month = jstDate.getUTCMonth() + 1;
|
|
83
|
+
const day = jstDate.getUTCDate();
|
|
84
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(
|
|
85
|
+
day
|
|
86
|
+
).padStart(2, "0")}`;
|
|
87
|
+
},
|
|
88
|
+
set(v) {},
|
|
89
|
+
},
|
|
90
|
+
billingMonth: {
|
|
91
|
+
configurable: true,
|
|
92
|
+
enumerable: true,
|
|
93
|
+
get() {
|
|
94
|
+
if (!this.billingDateAt) return null;
|
|
95
|
+
const jstDate = new Date(
|
|
96
|
+
this.billingDateAt.getTime() + 9 * 60 * 60 * 1000
|
|
97
|
+
); /* JST補正 */
|
|
98
|
+
const year = jstDate.getUTCFullYear();
|
|
99
|
+
const month = jstDate.getUTCMonth() + 1;
|
|
100
|
+
return `${year}-${String(month).padStart(2, "0")}`;
|
|
101
|
+
},
|
|
102
|
+
set(v) {},
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
Object.defineProperties(this, {
|
|
107
|
+
paymentDueDate: {
|
|
108
|
+
configurable: true,
|
|
109
|
+
enumerable: true,
|
|
110
|
+
get() {
|
|
111
|
+
if (!this.paymentDueDateAt) return null;
|
|
112
|
+
const jstDate = new Date(
|
|
113
|
+
this.paymentDueDateAt.getTime() + 9 * 60 * 60 * 1000
|
|
114
|
+
); /* JST補正 */
|
|
115
|
+
const year = jstDate.getUTCFullYear();
|
|
116
|
+
const month = jstDate.getUTCMonth() + 1;
|
|
117
|
+
const day = jstDate.getUTCDate();
|
|
118
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(
|
|
119
|
+
day
|
|
120
|
+
).padStart(2, "0")}`;
|
|
121
|
+
},
|
|
122
|
+
set(v) {},
|
|
123
|
+
},
|
|
124
|
+
paymentDueMonth: {
|
|
125
|
+
configurable: true,
|
|
126
|
+
enumerable: true,
|
|
127
|
+
get() {
|
|
128
|
+
if (!this.paymentDueDateAt) return null;
|
|
129
|
+
const jstDate = new Date(
|
|
130
|
+
this.paymentDueDateAt.getTime() + 9 * 60 * 60 * 1000
|
|
131
|
+
); /* JST補正 */
|
|
132
|
+
const year = jstDate.getUTCFullYear();
|
|
133
|
+
const month = jstDate.getUTCMonth() + 1;
|
|
134
|
+
return `${year}-${String(month).padStart(2, "0")}`;
|
|
135
|
+
},
|
|
136
|
+
set(v) {},
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
69
140
|
// 小計(税抜)を計算
|
|
70
141
|
Object.defineProperty(this, "subtotal", {
|
|
71
142
|
get() {
|
|
@@ -82,7 +153,9 @@ export default class Billing extends FireModel {
|
|
|
82
153
|
// 消費税額を計算
|
|
83
154
|
Object.defineProperty(this, "taxAmount", {
|
|
84
155
|
get() {
|
|
85
|
-
return
|
|
156
|
+
return this.operationResults.reduce((sum, item) => {
|
|
157
|
+
return sum + (item.tax || 0);
|
|
158
|
+
}, 0);
|
|
86
159
|
},
|
|
87
160
|
set() {},
|
|
88
161
|
enumerable: true,
|
|
@@ -100,7 +173,7 @@ export default class Billing extends FireModel {
|
|
|
100
173
|
});
|
|
101
174
|
|
|
102
175
|
// 表示用の明細サマリーを生成
|
|
103
|
-
Object.defineProperty(this, "
|
|
176
|
+
Object.defineProperty(this, "summary", {
|
|
104
177
|
get() {
|
|
105
178
|
return this.operationResults.map((item) => ({
|
|
106
179
|
operationResultId: item.docId,
|
package/src/Customer.js
CHANGED
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
* @static
|
|
30
30
|
* @prop {string} STATUS_ACTIVE - constant for active contract status
|
|
31
31
|
* @prop {string} STATUS_TERMINATED - constant for terminated contract status
|
|
32
|
+
*
|
|
33
|
+
* @method getPaymentDueDateAt
|
|
34
|
+
* @param {Date} baseDate - base date in UTC (JST - 9 hours)
|
|
35
|
+
* @returns {Date} payment due date in UTC (JST - 9 hours)
|
|
32
36
|
*****************************************************************************/
|
|
33
37
|
import FireModel from "@shisyamo4131/air-firebase-v2";
|
|
34
38
|
import { defField } from "./parts/fieldDefinitions.js";
|
|
@@ -99,6 +103,42 @@ export default class Customer extends FireModel {
|
|
|
99
103
|
prefecture: defAccessor("prefecture"),
|
|
100
104
|
});
|
|
101
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 支払期日を計算する
|
|
109
|
+
* @param {Date} baseDate - 基準日(JSTから9時間引いたUTC表現)
|
|
110
|
+
* @returns {Date} 支払期日(JSTから9時間引いたUTC表現)
|
|
111
|
+
*/
|
|
112
|
+
getPaymentDueDateAt(baseDate) {
|
|
113
|
+
// UTC → JST に変換(+9時間)
|
|
114
|
+
const jstDate = new Date(baseDate.getTime() + 9 * 60 * 60 * 1000);
|
|
115
|
+
|
|
116
|
+
// UTCメソッドでJST相当の年月を取得
|
|
117
|
+
const year = jstDate.getUTCFullYear();
|
|
118
|
+
const month = jstDate.getUTCMonth();
|
|
119
|
+
|
|
120
|
+
// paymentMonth分加算した年月を計算
|
|
121
|
+
const targetMonth = month + this.paymentMonth;
|
|
122
|
+
const targetYear = year + Math.floor(targetMonth / 12);
|
|
123
|
+
const finalMonth = targetMonth % 12;
|
|
124
|
+
|
|
125
|
+
let dueDate;
|
|
126
|
+
if (this.paymentDate === CutoffDate.VALUES.END_OF_MONTH) {
|
|
127
|
+
// 月末の場合
|
|
128
|
+
dueDate = new Date(Date.UTC(targetYear, finalMonth + 1, 0));
|
|
129
|
+
} else {
|
|
130
|
+
// 指定日の場合
|
|
131
|
+
dueDate = new Date(Date.UTC(targetYear, finalMonth, this.paymentDate));
|
|
132
|
+
|
|
133
|
+
// 指定日が存在しない場合は月末にする
|
|
134
|
+
if (dueDate.getUTCMonth() !== finalMonth) {
|
|
135
|
+
dueDate = new Date(Date.UTC(targetYear, finalMonth + 1, 0));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// JST → UTC に変換(-9時間)
|
|
140
|
+
return new Date(dueDate.getTime() - 9 * 60 * 60 * 1000);
|
|
141
|
+
}
|
|
102
142
|
}
|
|
103
143
|
|
|
104
144
|
/*****************************************************************************
|
package/src/OperationBilling.js
CHANGED
|
@@ -41,7 +41,6 @@
|
|
|
41
41
|
* - Setter: Splits array into employees and outsourcers based on `isEmployee` property
|
|
42
42
|
* @prop {string|null} siteOperationScheduleId - Associated SiteOperationSchedule document ID
|
|
43
43
|
* - If this OperationResult was created from a SiteOperationSchedule, this property holds that ID.
|
|
44
|
-
* - If this property is set, the instance cannot be deleted.
|
|
45
44
|
* @prop {boolean} useAdjustedQuantity - Flag to indicate if adjusted quantities are used for billing
|
|
46
45
|
* @prop {number} adjustedQuantityBase - Adjusted quantity for base workers
|
|
47
46
|
* - Quantity used for billing base workers when `useAdjustedQuantity` is true.
|
|
@@ -80,9 +79,12 @@
|
|
|
80
79
|
* @prop {number} overtimeWorkMinutes - Overtime work in minutes (read-only)
|
|
81
80
|
* - Calculated as `totalWorkMinutes` minus `regulationWorkMinutes`
|
|
82
81
|
* @prop {boolean} hasAgreement - Indicates if an Agreement is associated (read-only)
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* -
|
|
82
|
+
* - `true` if `agreement` is set, otherwise `false`.
|
|
83
|
+
* @prop {string|false} isInvalid - Validation status (read-only)
|
|
84
|
+
* - Returns false if valid.
|
|
85
|
+
* - Returns reason code string if invalid:
|
|
86
|
+
* - `EMPTY_BILLING_DATE`: Billing date is missing.
|
|
87
|
+
* - `EMPTY_AGREEMENT`: Agreement is missing and `allowEmptyAgreement` is false.
|
|
86
88
|
* @prop {Object} statistics - Statistics of workers (read-only)
|
|
87
89
|
* - Contains counts and total work minutes for base and qualified workers, including OJT breakdowns.
|
|
88
90
|
* - Structure: { base: {...}, qualified: {...}, total: {...} }
|
|
@@ -99,6 +101,8 @@
|
|
|
99
101
|
* - Calculated using the `Tax` utility based on `salesAmount` and `date`.
|
|
100
102
|
* @prop {number} billingAmount - Total billing amount including tax (read-only)
|
|
101
103
|
* - Sum of `salesAmount` and `tax`.
|
|
104
|
+
* @prop {string|null} billingDate - Billing date in YYYY-MM-DD format (read-only)
|
|
105
|
+
* - Returns a string in the format YYYY-MM-DD based on `billingDateAt`.
|
|
102
106
|
* @prop {string} billingMonth - Billing month in YYYY-MM format (read-only)
|
|
103
107
|
* @prop {Array<string>} employeeIds - Array of employee IDs from `employees` (read-only)
|
|
104
108
|
* @prop {Array<string>} outsourcerIds - Array of outsourcer IDs from `outsourcers` (read-only)
|
|
@@ -127,9 +131,6 @@
|
|
|
127
131
|
* @getter {number} endMinute - End minute (0-59) (read-only)
|
|
128
132
|
* - Extracted from `endTime`.
|
|
129
133
|
*
|
|
130
|
-
* @method beforeDelete - Override method to prevent deletion with siteOperationScheduleId
|
|
131
|
-
* - Prevents deletion if the instance has `siteOperationScheduleId`.
|
|
132
|
-
* - Throws an error if deletion is attempted on an instance created from SiteOperationSchedule.
|
|
133
134
|
* @method refreshBillingDateAt - Refresh billingDateAt based on dateAt and cutoffDate
|
|
134
135
|
* - Updates `billingDateAt` based on the current `dateAt` and `cutoffDate` values.
|
|
135
136
|
* @method addWorker - Adds a new worker (employee or outsourcer)
|
|
@@ -167,12 +168,20 @@
|
|
|
167
168
|
* - @param {Object|string} key - The combined key string or object
|
|
168
169
|
* - @returns {Array<string>} - Array containing [siteId, shiftType, date]
|
|
169
170
|
* - @throws {Error} - If the key is invalid.
|
|
171
|
+
* @method toggleLock - Toggle the lock status of an OperationResult document
|
|
172
|
+
* - @param {string} docId - Document ID
|
|
173
|
+
* - @param {boolean} value - Lock status value
|
|
174
|
+
* - @returns {Promise<void>}
|
|
170
175
|
*
|
|
171
|
-
* @override
|
|
176
|
+
* @override
|
|
177
|
+
* @method create - Override create method to indicate not implemented
|
|
172
178
|
* - Creation of OperationBilling instances is not implemented, as billing records are typically
|
|
173
179
|
* generated through the OperationResult class.
|
|
180
|
+
* @method update - Override update method to allow editing even when isLocked is true
|
|
181
|
+
* @method delete - Override delete method to allow deletion even when isLocked is true
|
|
174
182
|
*****************************************************************************/
|
|
175
183
|
import OperationResult from "./OperationResult.js";
|
|
184
|
+
import Operation from "./Operation.js";
|
|
176
185
|
|
|
177
186
|
export default class OperationBilling extends OperationResult {
|
|
178
187
|
static className = "稼働請求";
|
|
@@ -186,4 +195,45 @@ export default class OperationBilling extends OperationResult {
|
|
|
186
195
|
async create() {
|
|
187
196
|
return Promise.reject(new Error("[OperationBilling.js] Not implemented."));
|
|
188
197
|
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Override update method to allow editing even when isLocked is true
|
|
201
|
+
* @param {*} options
|
|
202
|
+
* @returns {Promise<void>}
|
|
203
|
+
*/
|
|
204
|
+
async update(options = {}) {
|
|
205
|
+
// isLockedのチェックをスキップして、親クラス(Operation)のupdateを直接呼び出す
|
|
206
|
+
return await Operation.prototype.update.call(this, options);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Override delete method to allow deletion even when isLocked is true
|
|
211
|
+
* @param {*} options
|
|
212
|
+
* @returns {Promise<void>}
|
|
213
|
+
*/
|
|
214
|
+
async delete(options = {}) {
|
|
215
|
+
// isLockedのチェックをスキップして、親クラス(Operation)のdeleteを直接呼び出す
|
|
216
|
+
return await Operation.prototype.delete.call(this, options);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Toggle the lock status of an OperationResult document
|
|
221
|
+
* @param {string} docId - Document ID
|
|
222
|
+
* @param {boolean} value - Lock status value
|
|
223
|
+
*/
|
|
224
|
+
static async toggleLock(docId, value) {
|
|
225
|
+
if (!docId || typeof docId !== "string") {
|
|
226
|
+
throw new Error("Invalid docId provided to toggleLock method");
|
|
227
|
+
}
|
|
228
|
+
if (typeof value !== "boolean") {
|
|
229
|
+
throw new Error("Invalid value provided to toggleLock method");
|
|
230
|
+
}
|
|
231
|
+
const instance = new OperationBilling();
|
|
232
|
+
const doc = await instance.fetchDoc({ docId });
|
|
233
|
+
if (!doc) {
|
|
234
|
+
throw new Error(`OperationResult document with ID ${docId} not found`);
|
|
235
|
+
}
|
|
236
|
+
doc.isLocked = value;
|
|
237
|
+
await doc.update();
|
|
238
|
+
}
|
|
189
239
|
}
|
package/src/OperationResult.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*
|
|
6
6
|
* - Extends Operation class to represent the result of an operation.
|
|
7
7
|
* - Also incorporates Agreement class properties for pricing and billing information.
|
|
8
|
-
* - Prevents deletion if the instance has `siteOperationScheduleId`.
|
|
9
8
|
* - Provides comprehensive billing calculations including statistics, sales amounts, and tax.
|
|
10
9
|
* - Supports both daily and hourly billing with adjusted quantities.
|
|
11
10
|
* - Automatically updates `billingDateAt` based on `dateAt` and `cutoffDate`.
|
|
@@ -41,7 +40,6 @@
|
|
|
41
40
|
* - Setter: Splits array into employees and outsourcers based on `isEmployee` property
|
|
42
41
|
* @prop {string|null} siteOperationScheduleId - Associated SiteOperationSchedule document ID
|
|
43
42
|
* - If this OperationResult was created from a SiteOperationSchedule, this property holds that ID.
|
|
44
|
-
* - If this property is set, the instance cannot be deleted.
|
|
45
43
|
* @prop {boolean} useAdjustedQuantity - Flag to indicate if adjusted quantities are used for billing
|
|
46
44
|
* @prop {number} adjustedQuantityBase - Adjusted quantity for base workers
|
|
47
45
|
* - Quantity used for billing base workers when `useAdjustedQuantity` is true.
|
|
@@ -79,9 +77,12 @@
|
|
|
79
77
|
* @prop {number} overtimeWorkMinutes - Overtime work in minutes (read-only)
|
|
80
78
|
* - Calculated as `totalWorkMinutes` minus `regulationWorkMinutes`
|
|
81
79
|
* @prop {boolean} hasAgreement - Indicates if an Agreement is associated (read-only)
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* -
|
|
80
|
+
* - `true` if `agreement` is set, otherwise `false`.
|
|
81
|
+
* @prop {string|false} isInvalid - Validation status (read-only)
|
|
82
|
+
* - Returns false if valid.
|
|
83
|
+
* - Returns reason code string if invalid:
|
|
84
|
+
* - `EMPTY_BILLING_DATE`: Billing date is missing.
|
|
85
|
+
* - `EMPTY_AGREEMENT`: Agreement is missing and `allowEmptyAgreement` is false.
|
|
85
86
|
* @prop {Object} statistics - Statistics of workers (read-only)
|
|
86
87
|
* - Contains counts and total work minutes for base and qualified workers, including OJT breakdowns.
|
|
87
88
|
* - Structure: { base: {...}, qualified: {...}, total: {...} }
|
|
@@ -98,6 +99,8 @@
|
|
|
98
99
|
* - Calculated using the `Tax` utility based on `salesAmount` and `date`.
|
|
99
100
|
* @prop {number} billingAmount - Total billing amount including tax (read-only)
|
|
100
101
|
* - Sum of `salesAmount` and `tax`.
|
|
102
|
+
* @prop {string|null} billingDate - Billing date in YYYY-MM-DD format (read-only)
|
|
103
|
+
* - Returns a string in the format YYYY-MM-DD based on `billingDateAt`.
|
|
101
104
|
* @prop {string} billingMonth - Billing month in YYYY-MM format (read-only)
|
|
102
105
|
* @prop {Array<string>} employeeIds - Array of employee IDs from `employees` (read-only)
|
|
103
106
|
* @prop {Array<string>} outsourcerIds - Array of outsourcer IDs from `outsourcers` (read-only)
|
|
@@ -126,9 +129,6 @@
|
|
|
126
129
|
* @getter {number} endMinute - End minute (0-59) (read-only)
|
|
127
130
|
* - Extracted from `endTime`.
|
|
128
131
|
*
|
|
129
|
-
* @method beforeDelete - Override method to prevent deletion with siteOperationScheduleId
|
|
130
|
-
* - Prevents deletion if the instance has `siteOperationScheduleId`.
|
|
131
|
-
* - Throws an error if deletion is attempted on an instance created from SiteOperationSchedule.
|
|
132
132
|
* @method refreshBillingDateAt - Refresh billingDateAt based on dateAt and cutoffDate
|
|
133
133
|
* - Updates `billingDateAt` based on the current `dateAt` and `cutoffDate` values.
|
|
134
134
|
* @method addWorker - Adds a new worker (employee or outsourcer)
|
|
@@ -167,7 +167,11 @@
|
|
|
167
167
|
* - @returns {Array<string>} - Array containing [siteId, shiftType, date]
|
|
168
168
|
* - @throws {Error} - If the key is invalid.
|
|
169
169
|
*
|
|
170
|
-
* @override
|
|
170
|
+
* @override
|
|
171
|
+
* @method setDateAtCallback - Updates `billingDateAt` based on the new `dateAt` value.
|
|
172
|
+
* @method beforeCreate - Override to sync customerId from siteId
|
|
173
|
+
* @method beforeUpdate - Override to sync customerId from siteId when siteId changes
|
|
174
|
+
* @method beforeDelete - Override to prevent deletion if isLocked is true
|
|
171
175
|
*****************************************************************************/
|
|
172
176
|
import Operation from "./Operation.js";
|
|
173
177
|
import Agreement from "./Agreement.js";
|
|
@@ -178,6 +182,7 @@ import Tax from "./tax.js";
|
|
|
178
182
|
import { BILLING_UNIT_TYPE_PER_HOUR } from "./constants/billing-unit-type.js";
|
|
179
183
|
import RoundSetting from "./RoundSetting.js";
|
|
180
184
|
import CutoffDate from "./utils/CutoffDate.js";
|
|
185
|
+
import Site from "./Site.js";
|
|
181
186
|
|
|
182
187
|
const classProps = {
|
|
183
188
|
...Operation.classProps,
|
|
@@ -202,7 +207,7 @@ const classProps = {
|
|
|
202
207
|
label: "資格残業(調整)",
|
|
203
208
|
default: 0,
|
|
204
209
|
}),
|
|
205
|
-
billingDateAt: defField("dateAt", { label: "
|
|
210
|
+
billingDateAt: defField("dateAt", { label: "請求締日" }),
|
|
206
211
|
employees: defField("array", { customClass: OperationResultDetail }),
|
|
207
212
|
outsourcers: defField("array", {
|
|
208
213
|
customClass: OperationResultDetail,
|
|
@@ -213,9 +218,21 @@ const classProps = {
|
|
|
213
218
|
}),
|
|
214
219
|
agreement: defField("object", { label: "取極め", customClass: Agreement }),
|
|
215
220
|
allowEmptyAgreement: defField("check", {
|
|
216
|
-
label: "
|
|
221
|
+
label: "取極めなしを許容",
|
|
217
222
|
default: false,
|
|
218
223
|
}),
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* siteId から自動同期されるプロパティ
|
|
227
|
+
* - 従属する取引先の変更を不可としているため、ドキュメントの更新時に取得するだけで問題ない。
|
|
228
|
+
* - 但し、siteId が変更された時は再取得する必要がある。
|
|
229
|
+
*/
|
|
230
|
+
customerId: defField("customerId", { required: true, hidden: true }),
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const INVALID_REASON = {
|
|
234
|
+
EMPTY_BILLING_DATE: "EMPTY_BILLING_DATE",
|
|
235
|
+
EMPTY_AGREEMENT: "EMPTY_AGREEMENT",
|
|
219
236
|
};
|
|
220
237
|
|
|
221
238
|
export default class OperationResult extends Operation {
|
|
@@ -237,7 +254,6 @@ export default class OperationResult extends Operation {
|
|
|
237
254
|
super.afterInitialize();
|
|
238
255
|
|
|
239
256
|
/** Computed properties */
|
|
240
|
-
let _agreement = this.agreement;
|
|
241
257
|
Object.defineProperties(this, {
|
|
242
258
|
statistics: {
|
|
243
259
|
configurable: true,
|
|
@@ -408,7 +424,7 @@ export default class OperationResult extends Operation {
|
|
|
408
424
|
},
|
|
409
425
|
set(v) {},
|
|
410
426
|
},
|
|
411
|
-
|
|
427
|
+
billingDate: {
|
|
412
428
|
configurable: true,
|
|
413
429
|
enumerable: true,
|
|
414
430
|
get() {
|
|
@@ -418,21 +434,26 @@ export default class OperationResult extends Operation {
|
|
|
418
434
|
); /* JST補正 */
|
|
419
435
|
const year = jstDate.getUTCFullYear();
|
|
420
436
|
const month = jstDate.getUTCMonth() + 1;
|
|
421
|
-
|
|
437
|
+
const day = jstDate.getUTCDate();
|
|
438
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(
|
|
439
|
+
day
|
|
440
|
+
).padStart(2, "0")}`;
|
|
422
441
|
},
|
|
423
442
|
set(v) {},
|
|
424
443
|
},
|
|
425
|
-
|
|
426
|
-
agreement: {
|
|
444
|
+
billingMonth: {
|
|
427
445
|
configurable: true,
|
|
428
446
|
enumerable: true,
|
|
429
447
|
get() {
|
|
430
|
-
return
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
448
|
+
if (!this.billingDateAt) return null;
|
|
449
|
+
const jstDate = new Date(
|
|
450
|
+
this.billingDateAt.getTime() + 9 * 60 * 60 * 1000
|
|
451
|
+
); /* JST補正 */
|
|
452
|
+
const year = jstDate.getUTCFullYear();
|
|
453
|
+
const month = jstDate.getUTCMonth() + 1;
|
|
454
|
+
return `${year}-${String(month).padStart(2, "0")}`;
|
|
435
455
|
},
|
|
456
|
+
set(v) {},
|
|
436
457
|
},
|
|
437
458
|
hasAgreement: {
|
|
438
459
|
configurable: true,
|
|
@@ -442,16 +463,40 @@ export default class OperationResult extends Operation {
|
|
|
442
463
|
},
|
|
443
464
|
set(v) {},
|
|
444
465
|
},
|
|
445
|
-
|
|
466
|
+
isInvalid: {
|
|
446
467
|
configurable: true,
|
|
447
468
|
enumerable: true,
|
|
448
469
|
get() {
|
|
449
|
-
if (this.
|
|
450
|
-
|
|
470
|
+
if (!this.agreement && !this.allowEmptyAgreement) {
|
|
471
|
+
return INVALID_REASON.EMPTY_AGREEMENT;
|
|
472
|
+
}
|
|
473
|
+
if (!this.billingDateAt) {
|
|
474
|
+
return INVALID_REASON.EMPTY_BILLING_DATE;
|
|
475
|
+
}
|
|
476
|
+
return false;
|
|
451
477
|
},
|
|
452
478
|
set(v) {},
|
|
453
479
|
},
|
|
454
480
|
});
|
|
481
|
+
|
|
482
|
+
/** Triggers */
|
|
483
|
+
let _agreement = this.agreement;
|
|
484
|
+
Object.defineProperties(this, {
|
|
485
|
+
agreement: {
|
|
486
|
+
configurable: true,
|
|
487
|
+
enumerable: true,
|
|
488
|
+
get() {
|
|
489
|
+
return _agreement;
|
|
490
|
+
},
|
|
491
|
+
set(v) {
|
|
492
|
+
const oldKey = _agreement ? _agreement.key : null;
|
|
493
|
+
const newKey = v ? v.key : null;
|
|
494
|
+
if (oldKey === newKey) return;
|
|
495
|
+
_agreement = v;
|
|
496
|
+
this.refreshBillingDateAt();
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
});
|
|
455
500
|
}
|
|
456
501
|
|
|
457
502
|
/**
|
|
@@ -487,50 +532,65 @@ export default class OperationResult extends Operation {
|
|
|
487
532
|
}
|
|
488
533
|
|
|
489
534
|
/**
|
|
490
|
-
*
|
|
491
|
-
* @
|
|
492
|
-
* @
|
|
535
|
+
* Synchronize customerId from siteId
|
|
536
|
+
* @returns {Promise<void>}
|
|
537
|
+
* @throws {Error} If the specified siteId does not exist
|
|
493
538
|
*/
|
|
494
|
-
async
|
|
495
|
-
if (
|
|
539
|
+
async _syncCustomerId() {
|
|
540
|
+
if (!this.siteId) return;
|
|
541
|
+
const siteInstance = new Site();
|
|
542
|
+
const siteExists = await siteInstance.fetch({ docId: this.siteId });
|
|
543
|
+
if (!siteExists) {
|
|
496
544
|
throw new Error(
|
|
497
|
-
|
|
545
|
+
`[OperationResult] The specified siteId (${this.siteId}) does not exist.`
|
|
498
546
|
);
|
|
499
547
|
}
|
|
500
|
-
|
|
548
|
+
this.customerId = siteInstance.customerId;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Override beforeCreate to sync customerId
|
|
553
|
+
* @returns {Promise<void>}
|
|
554
|
+
*/
|
|
555
|
+
async beforeCreate() {
|
|
556
|
+
await super.beforeCreate();
|
|
557
|
+
|
|
558
|
+
// Sync customerId
|
|
559
|
+
await this._syncCustomerId();
|
|
501
560
|
}
|
|
502
561
|
|
|
503
562
|
/**
|
|
504
|
-
* Override
|
|
505
|
-
* - Also validate billingDateAt when allowEmptyAgreement is true
|
|
506
|
-
* @param {*} options
|
|
563
|
+
* Override beforeUpdate to sync customerId if siteId changed
|
|
507
564
|
* @returns {Promise<void>}
|
|
508
565
|
*/
|
|
509
|
-
async
|
|
566
|
+
async beforeUpdate() {
|
|
567
|
+
await super.beforeUpdate();
|
|
568
|
+
|
|
569
|
+
// Prevent editing if isLocked is true
|
|
510
570
|
if (this.isLocked) {
|
|
511
571
|
throw new Error(
|
|
512
572
|
"[OperationResult] This OperationResult is locked and cannot be edited."
|
|
513
573
|
);
|
|
514
574
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
520
|
-
return await super.update(options);
|
|
575
|
+
|
|
576
|
+
// Sync customerId if siteId changed
|
|
577
|
+
if (this.siteId === this._beforeData.siteId && this.customerId) return;
|
|
578
|
+
await this._syncCustomerId();
|
|
521
579
|
}
|
|
522
580
|
|
|
523
581
|
/**
|
|
524
|
-
* Override
|
|
525
|
-
* @param {*} options
|
|
582
|
+
* Override beforeDelete to prevent deletion if isLocked is true
|
|
526
583
|
* @returns {Promise<void>}
|
|
584
|
+
* @throws {Error} If isLocked is true
|
|
527
585
|
*/
|
|
528
|
-
async
|
|
586
|
+
async beforeDelete() {
|
|
587
|
+
await super.beforeDelete();
|
|
588
|
+
|
|
589
|
+
// Prevent deletion if isLocked is true
|
|
529
590
|
if (this.isLocked) {
|
|
530
591
|
throw new Error(
|
|
531
592
|
"[OperationResult] This OperationResult is locked and cannot be deleted."
|
|
532
593
|
);
|
|
533
594
|
}
|
|
534
|
-
return await super.delete(options);
|
|
535
595
|
}
|
|
536
596
|
}
|
package/src/Site.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file src/Site.js
|
|
3
3
|
* @author shisyamo4131
|
|
4
|
-
* @version 1.
|
|
4
|
+
* @version 1.1.0
|
|
5
|
+
* @update 2025-11-20 version 0.2.0-bata
|
|
6
|
+
* - Prevent changing customer reference on update.
|
|
7
|
+
* - Move `customer` property to the top of classProps for better visibility.
|
|
5
8
|
*/
|
|
6
9
|
import { default as FireModel } from "@shisyamo4131/air-firebase-v2";
|
|
7
10
|
import { defField } from "./parts/fieldDefinitions.js";
|
|
@@ -12,6 +15,16 @@ import Agreement from "./Agreement.js";
|
|
|
12
15
|
import { VALUES } from "./constants/site-status.js";
|
|
13
16
|
|
|
14
17
|
const classProps = {
|
|
18
|
+
customer: defField("customer", {
|
|
19
|
+
required: true,
|
|
20
|
+
customClass: CustomerMinimal,
|
|
21
|
+
component: {
|
|
22
|
+
attrs: {
|
|
23
|
+
api: () => fetchDocsApi(CustomerMinimal),
|
|
24
|
+
noFilter: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}),
|
|
15
28
|
code: defField("code", { label: "現場コード" }),
|
|
16
29
|
name: defField("name", {
|
|
17
30
|
label: "現場名",
|
|
@@ -29,16 +42,6 @@ const classProps = {
|
|
|
29
42
|
address: defField("address", { required: true }),
|
|
30
43
|
building: defField("building"),
|
|
31
44
|
location: defField("location"),
|
|
32
|
-
customer: defField("customer", {
|
|
33
|
-
required: true,
|
|
34
|
-
customClass: CustomerMinimal,
|
|
35
|
-
component: {
|
|
36
|
-
attrs: {
|
|
37
|
-
api: () => fetchDocsApi(CustomerMinimal),
|
|
38
|
-
noFilter: true,
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
}),
|
|
42
45
|
remarks: defField("multipleLine", { label: "備考" }),
|
|
43
46
|
agreements: defField("array", { label: "取極め", customClass: Agreement }),
|
|
44
47
|
status: defField("siteStatus", { required: true }),
|
|
@@ -115,6 +118,19 @@ export default class Site extends FireModel {
|
|
|
115
118
|
static STATUS_ACTIVE = VALUES.ACTIVE.value;
|
|
116
119
|
static STATUS_TERMINATED = VALUES.TERMINATED.value;
|
|
117
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Override beforeUpdate to prevent changing customer reference.
|
|
123
|
+
* @returns {Promise<void>}
|
|
124
|
+
*/
|
|
125
|
+
async beforeUpdate() {
|
|
126
|
+
await super.beforeUpdate();
|
|
127
|
+
if (this.customer.docId !== this._beforeData.customer.docId) {
|
|
128
|
+
return Promise.reject(
|
|
129
|
+
new Error("Not allowed to change customer reference.")
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
118
134
|
afterInitialize(item = {}) {
|
|
119
135
|
super.afterInitialize(item);
|
|
120
136
|
|
|
@@ -210,6 +210,18 @@ export const fieldDefinitions = {
|
|
|
210
210
|
label: "市区町村",
|
|
211
211
|
length: 10,
|
|
212
212
|
},
|
|
213
|
+
customerId: {
|
|
214
|
+
...generalDefinitions.oneLine,
|
|
215
|
+
label: "取引先",
|
|
216
|
+
component: {
|
|
217
|
+
name: "air-autocomplete-api",
|
|
218
|
+
attrs: {
|
|
219
|
+
itemValue: "docId",
|
|
220
|
+
itemTitle: "name",
|
|
221
|
+
noFilter: true,
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
213
225
|
displayName: {
|
|
214
226
|
...generalDefinitions.oneLine,
|
|
215
227
|
label: "表示名",
|