@shisyamo4131/air-guard-v2-schemas 1.3.1-dev.8 → 2.0.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.
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/ArrangementNotification.js +17 -3
- package/src/Billing.js +82 -9
- package/src/Customer.js +40 -0
- package/src/OperationBilling.js +62 -8
- package/src/OperationResult.js +110 -47
- 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
|
@@ -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.
|
|
@@ -56,6 +55,10 @@
|
|
|
56
55
|
* @prop {boolean} isLocked - Lock flag
|
|
57
56
|
* - When set to true, the OperationResult is locked from edits exept for editing as OperationBilling.
|
|
58
57
|
* @prop {Agreement|null} agreement - Associated Agreement object
|
|
58
|
+
* - The Agreement instance associated with this OperationResult for pricing and billing information.
|
|
59
|
+
* - When set, it influences billing calculations such as unit prices and billing dates.
|
|
60
|
+
* @prop {boolean} allowEmptyAgreement - Flag to ignore missing Agreement
|
|
61
|
+
* - When set to true, allows the OperationResult to be valid even if no Agreement is associated.
|
|
59
62
|
*
|
|
60
63
|
* @readonly
|
|
61
64
|
* @prop {string} date - Date string in YYYY-MM-DD format based on `dateAt` (read-only)
|
|
@@ -76,9 +79,12 @@
|
|
|
76
79
|
* @prop {number} overtimeWorkMinutes - Overtime work in minutes (read-only)
|
|
77
80
|
* - Calculated as `totalWorkMinutes` minus `regulationWorkMinutes`
|
|
78
81
|
* @prop {boolean} hasAgreement - Indicates if an Agreement is associated (read-only)
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* -
|
|
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.
|
|
82
88
|
* @prop {Object} statistics - Statistics of workers (read-only)
|
|
83
89
|
* - Contains counts and total work minutes for base and qualified workers, including OJT breakdowns.
|
|
84
90
|
* - Structure: { base: {...}, qualified: {...}, total: {...} }
|
|
@@ -95,6 +101,8 @@
|
|
|
95
101
|
* - Calculated using the `Tax` utility based on `salesAmount` and `date`.
|
|
96
102
|
* @prop {number} billingAmount - Total billing amount including tax (read-only)
|
|
97
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`.
|
|
98
106
|
* @prop {string} billingMonth - Billing month in YYYY-MM format (read-only)
|
|
99
107
|
* @prop {Array<string>} employeeIds - Array of employee IDs from `employees` (read-only)
|
|
100
108
|
* @prop {Array<string>} outsourcerIds - Array of outsourcer IDs from `outsourcers` (read-only)
|
|
@@ -123,9 +131,6 @@
|
|
|
123
131
|
* @getter {number} endMinute - End minute (0-59) (read-only)
|
|
124
132
|
* - Extracted from `endTime`.
|
|
125
133
|
*
|
|
126
|
-
* @method beforeDelete - Override method to prevent deletion with siteOperationScheduleId
|
|
127
|
-
* - Prevents deletion if the instance has `siteOperationScheduleId`.
|
|
128
|
-
* - Throws an error if deletion is attempted on an instance created from SiteOperationSchedule.
|
|
129
134
|
* @method refreshBillingDateAt - Refresh billingDateAt based on dateAt and cutoffDate
|
|
130
135
|
* - Updates `billingDateAt` based on the current `dateAt` and `cutoffDate` values.
|
|
131
136
|
* @method addWorker - Adds a new worker (employee or outsourcer)
|
|
@@ -163,12 +168,20 @@
|
|
|
163
168
|
* - @param {Object|string} key - The combined key string or object
|
|
164
169
|
* - @returns {Array<string>} - Array containing [siteId, shiftType, date]
|
|
165
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>}
|
|
166
175
|
*
|
|
167
|
-
* @override
|
|
176
|
+
* @override
|
|
177
|
+
* @method create - Override create method to indicate not implemented
|
|
168
178
|
* - Creation of OperationBilling instances is not implemented, as billing records are typically
|
|
169
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
|
|
170
182
|
*****************************************************************************/
|
|
171
183
|
import OperationResult from "./OperationResult.js";
|
|
184
|
+
import Operation from "./Operation.js";
|
|
172
185
|
|
|
173
186
|
export default class OperationBilling extends OperationResult {
|
|
174
187
|
static className = "稼働請求";
|
|
@@ -182,4 +195,45 @@ export default class OperationBilling extends OperationResult {
|
|
|
182
195
|
async create() {
|
|
183
196
|
return Promise.reject(new Error("[OperationBilling.js] Not implemented."));
|
|
184
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
|
+
}
|
|
185
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.
|
|
@@ -56,7 +54,10 @@
|
|
|
56
54
|
* @prop {boolean} isLocked - Lock flag
|
|
57
55
|
* - When set to true, the OperationResult is locked from edits exept for editing as OperationBilling.
|
|
58
56
|
* @prop {Agreement|null} agreement - Associated Agreement object
|
|
59
|
-
*
|
|
57
|
+
* - The Agreement instance associated with this OperationResult for pricing and billing information.
|
|
58
|
+
* - When set, it influences billing calculations such as unit prices and billing dates.
|
|
59
|
+
* @prop {boolean} allowEmptyAgreement - Flag to ignore missing Agreement
|
|
60
|
+
* - When set to true, allows the OperationResult to be valid even if no Agreement is associated.
|
|
60
61
|
* @readonly
|
|
61
62
|
* @prop {string} date - Date string in YYYY-MM-DD format based on `dateAt` (read-only)
|
|
62
63
|
* - Returns a string in the format YYYY-MM-DD based on `dateAt`.
|
|
@@ -76,9 +77,12 @@
|
|
|
76
77
|
* @prop {number} overtimeWorkMinutes - Overtime work in minutes (read-only)
|
|
77
78
|
* - Calculated as `totalWorkMinutes` minus `regulationWorkMinutes`
|
|
78
79
|
* @prop {boolean} hasAgreement - Indicates if an Agreement is associated (read-only)
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* -
|
|
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.
|
|
82
86
|
* @prop {Object} statistics - Statistics of workers (read-only)
|
|
83
87
|
* - Contains counts and total work minutes for base and qualified workers, including OJT breakdowns.
|
|
84
88
|
* - Structure: { base: {...}, qualified: {...}, total: {...} }
|
|
@@ -95,6 +99,8 @@
|
|
|
95
99
|
* - Calculated using the `Tax` utility based on `salesAmount` and `date`.
|
|
96
100
|
* @prop {number} billingAmount - Total billing amount including tax (read-only)
|
|
97
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`.
|
|
98
104
|
* @prop {string} billingMonth - Billing month in YYYY-MM format (read-only)
|
|
99
105
|
* @prop {Array<string>} employeeIds - Array of employee IDs from `employees` (read-only)
|
|
100
106
|
* @prop {Array<string>} outsourcerIds - Array of outsourcer IDs from `outsourcers` (read-only)
|
|
@@ -123,9 +129,6 @@
|
|
|
123
129
|
* @getter {number} endMinute - End minute (0-59) (read-only)
|
|
124
130
|
* - Extracted from `endTime`.
|
|
125
131
|
*
|
|
126
|
-
* @method beforeDelete - Override method to prevent deletion with siteOperationScheduleId
|
|
127
|
-
* - Prevents deletion if the instance has `siteOperationScheduleId`.
|
|
128
|
-
* - Throws an error if deletion is attempted on an instance created from SiteOperationSchedule.
|
|
129
132
|
* @method refreshBillingDateAt - Refresh billingDateAt based on dateAt and cutoffDate
|
|
130
133
|
* - Updates `billingDateAt` based on the current `dateAt` and `cutoffDate` values.
|
|
131
134
|
* @method addWorker - Adds a new worker (employee or outsourcer)
|
|
@@ -164,7 +167,11 @@
|
|
|
164
167
|
* - @returns {Array<string>} - Array containing [siteId, shiftType, date]
|
|
165
168
|
* - @throws {Error} - If the key is invalid.
|
|
166
169
|
*
|
|
167
|
-
* @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
|
|
168
175
|
*****************************************************************************/
|
|
169
176
|
import Operation from "./Operation.js";
|
|
170
177
|
import Agreement from "./Agreement.js";
|
|
@@ -175,6 +182,7 @@ import Tax from "./tax.js";
|
|
|
175
182
|
import { BILLING_UNIT_TYPE_PER_HOUR } from "./constants/billing-unit-type.js";
|
|
176
183
|
import RoundSetting from "./RoundSetting.js";
|
|
177
184
|
import CutoffDate from "./utils/CutoffDate.js";
|
|
185
|
+
import Site from "./Site.js";
|
|
178
186
|
|
|
179
187
|
const classProps = {
|
|
180
188
|
...Operation.classProps,
|
|
@@ -199,7 +207,7 @@ const classProps = {
|
|
|
199
207
|
label: "資格残業(調整)",
|
|
200
208
|
default: 0,
|
|
201
209
|
}),
|
|
202
|
-
billingDateAt: defField("dateAt", { label: "
|
|
210
|
+
billingDateAt: defField("dateAt", { label: "請求締日" }),
|
|
203
211
|
employees: defField("array", { customClass: OperationResultDetail }),
|
|
204
212
|
outsourcers: defField("array", {
|
|
205
213
|
customClass: OperationResultDetail,
|
|
@@ -209,10 +217,22 @@ const classProps = {
|
|
|
209
217
|
default: false,
|
|
210
218
|
}),
|
|
211
219
|
agreement: defField("object", { label: "取極め", customClass: Agreement }),
|
|
212
|
-
|
|
213
|
-
label: "
|
|
220
|
+
allowEmptyAgreement: defField("check", {
|
|
221
|
+
label: "取極めなしを許容",
|
|
214
222
|
default: false,
|
|
215
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",
|
|
216
236
|
};
|
|
217
237
|
|
|
218
238
|
export default class OperationResult extends Operation {
|
|
@@ -234,7 +254,6 @@ export default class OperationResult extends Operation {
|
|
|
234
254
|
super.afterInitialize();
|
|
235
255
|
|
|
236
256
|
/** Computed properties */
|
|
237
|
-
let _agreement = this.agreement;
|
|
238
257
|
Object.defineProperties(this, {
|
|
239
258
|
statistics: {
|
|
240
259
|
configurable: true,
|
|
@@ -405,7 +424,7 @@ export default class OperationResult extends Operation {
|
|
|
405
424
|
},
|
|
406
425
|
set(v) {},
|
|
407
426
|
},
|
|
408
|
-
|
|
427
|
+
billingDate: {
|
|
409
428
|
configurable: true,
|
|
410
429
|
enumerable: true,
|
|
411
430
|
get() {
|
|
@@ -415,21 +434,26 @@ export default class OperationResult extends Operation {
|
|
|
415
434
|
); /* JST補正 */
|
|
416
435
|
const year = jstDate.getUTCFullYear();
|
|
417
436
|
const month = jstDate.getUTCMonth() + 1;
|
|
418
|
-
|
|
437
|
+
const day = jstDate.getUTCDate();
|
|
438
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(
|
|
439
|
+
day
|
|
440
|
+
).padStart(2, "0")}`;
|
|
419
441
|
},
|
|
420
442
|
set(v) {},
|
|
421
443
|
},
|
|
422
|
-
|
|
423
|
-
agreement: {
|
|
444
|
+
billingMonth: {
|
|
424
445
|
configurable: true,
|
|
425
446
|
enumerable: true,
|
|
426
447
|
get() {
|
|
427
|
-
return
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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")}`;
|
|
432
455
|
},
|
|
456
|
+
set(v) {},
|
|
433
457
|
},
|
|
434
458
|
hasAgreement: {
|
|
435
459
|
configurable: true,
|
|
@@ -439,16 +463,40 @@ export default class OperationResult extends Operation {
|
|
|
439
463
|
},
|
|
440
464
|
set(v) {},
|
|
441
465
|
},
|
|
442
|
-
|
|
466
|
+
isInvalid: {
|
|
443
467
|
configurable: true,
|
|
444
468
|
enumerable: true,
|
|
445
469
|
get() {
|
|
446
|
-
if (this.
|
|
447
|
-
|
|
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;
|
|
448
477
|
},
|
|
449
478
|
set(v) {},
|
|
450
479
|
},
|
|
451
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
|
+
});
|
|
452
500
|
}
|
|
453
501
|
|
|
454
502
|
/**
|
|
@@ -484,50 +532,65 @@ export default class OperationResult extends Operation {
|
|
|
484
532
|
}
|
|
485
533
|
|
|
486
534
|
/**
|
|
487
|
-
*
|
|
488
|
-
* @
|
|
489
|
-
* @
|
|
535
|
+
* Synchronize customerId from siteId
|
|
536
|
+
* @returns {Promise<void>}
|
|
537
|
+
* @throws {Error} If the specified siteId does not exist
|
|
490
538
|
*/
|
|
491
|
-
async
|
|
492
|
-
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) {
|
|
493
544
|
throw new Error(
|
|
494
|
-
|
|
545
|
+
`[OperationResult] The specified siteId (${this.siteId}) does not exist.`
|
|
495
546
|
);
|
|
496
547
|
}
|
|
497
|
-
|
|
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();
|
|
498
560
|
}
|
|
499
561
|
|
|
500
562
|
/**
|
|
501
|
-
* Override
|
|
502
|
-
* - Also validate billingDateAt when ignoreEmptyAgreement is true
|
|
503
|
-
* @param {*} options
|
|
563
|
+
* Override beforeUpdate to sync customerId if siteId changed
|
|
504
564
|
* @returns {Promise<void>}
|
|
505
565
|
*/
|
|
506
|
-
async
|
|
566
|
+
async beforeUpdate() {
|
|
567
|
+
await super.beforeUpdate();
|
|
568
|
+
|
|
569
|
+
// Prevent editing if isLocked is true
|
|
507
570
|
if (this.isLocked) {
|
|
508
571
|
throw new Error(
|
|
509
572
|
"[OperationResult] This OperationResult is locked and cannot be edited."
|
|
510
573
|
);
|
|
511
574
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
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();
|
|
518
579
|
}
|
|
519
580
|
|
|
520
581
|
/**
|
|
521
|
-
* Override
|
|
522
|
-
* @param {*} options
|
|
582
|
+
* Override beforeDelete to prevent deletion if isLocked is true
|
|
523
583
|
* @returns {Promise<void>}
|
|
584
|
+
* @throws {Error} If isLocked is true
|
|
524
585
|
*/
|
|
525
|
-
async
|
|
586
|
+
async beforeDelete() {
|
|
587
|
+
await super.beforeDelete();
|
|
588
|
+
|
|
589
|
+
// Prevent deletion if isLocked is true
|
|
526
590
|
if (this.isLocked) {
|
|
527
591
|
throw new Error(
|
|
528
592
|
"[OperationResult] This OperationResult is locked and cannot be deleted."
|
|
529
593
|
);
|
|
530
594
|
}
|
|
531
|
-
return await super.delete(options);
|
|
532
595
|
}
|
|
533
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: "表示名",
|