@shisyamo4131/air-guard-v2-schemas 2.3.8-dev.2 → 2.4.1-dev.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 CHANGED
@@ -6,6 +6,7 @@ export { default as Company } from "./src/Company.js";
6
6
  export { default as Customer, CustomerMinimal } from "./src/Customer.js";
7
7
  export { default as CutoffDate } from "./src/utils/CutoffDate.js";
8
8
  export { default as Employee } from "./src/Employee.js";
9
+ export { GeocodableMixin } from "./src/mixins/GeocodableMixin.js";
9
10
  export { default as OperationBilling } from "./src/OperationBilling.js";
10
11
  export { default as OperationResult } from "./src/OperationResult.js";
11
12
  export { default as OperationResultDetail } from "./src/OperationResultDetail.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shisyamo4131/air-guard-v2-schemas",
3
- "version": "2.3.8-dev.2",
3
+ "version": "2.4.1-dev.0",
4
4
  "description": "Schemas for AirGuard V2",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/src/Company.js CHANGED
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Company Model
3
- * @version 1.4.0
3
+ * @version 1.0.0
4
4
  * @author shisyamo4131
5
- * @update 2025-12-02 Add maintenance information properties.
6
- * @update 2025-12-01 Add Stripe integration fields (stripeCustomerId, subscription).
7
- * @update 2025-11-27 Add bank information fields for billing.
8
- * @update 2025-11-23 Set `usePrefix` to false.
5
+ * @update 2025-12-29 - Add `isCompleteRequiredFields` computed property.
6
+ * @update 2025-12-02 - Add maintenance information properties.
7
+ * @update 2025-12-01 - Add Stripe integration fields (stripeCustomerId, subscription).
8
+ * @update 2025-11-27 - Add bank information fields for billing.
9
+ * @update 2025-11-23 - Set `usePrefix` to false.
9
10
  */
10
11
  import FireModel from "@shisyamo4131/air-firebase-v2";
11
12
  import { defField } from "./parts/fieldDefinitions.js";
@@ -13,6 +14,7 @@ import { defAccessor } from "./parts/accessorDefinitions.js";
13
14
  import Agreement from "./Agreement.js";
14
15
  import SiteOrder from "./SiteOrder.js";
15
16
  import RoundSetting from "./RoundSetting.js";
17
+ import { GeocodableMixin } from "./mixins/GeocodableMixin.js";
16
18
 
17
19
  const classProps = {
18
20
  companyName: defField("name", { label: "会社名", required: true }),
@@ -120,7 +122,7 @@ const classProps = {
120
122
  maintenanceStartedBy: defField("oneLine", { default: null, hidden: true }),
121
123
  };
122
124
 
123
- export default class Company extends FireModel {
125
+ export default class Company extends GeocodableMixin(FireModel) {
124
126
  static className = "会社";
125
127
  static collectionPath = "Companies";
126
128
  static usePrefix = false;
@@ -149,6 +151,25 @@ export default class Company extends FireModel {
149
151
  },
150
152
  set() {},
151
153
  },
154
+
155
+ // Check if all `required` fields are filled.
156
+ // Used for validating whether the Company info is complete.
157
+ isCompleteRequiredFields: {
158
+ enumerable: true,
159
+ configurable: true,
160
+ get() {
161
+ return !!(
162
+ this.companyName &&
163
+ this.companyNameKana &&
164
+ this.zipcode &&
165
+ this.prefCode &&
166
+ this.city &&
167
+ this.address &&
168
+ this.tel
169
+ );
170
+ },
171
+ set() {},
172
+ },
152
173
  });
153
174
 
154
175
  /*************************************************************************
package/src/Customer.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  CONTRACT_STATUS_VALUES,
15
15
  PAYMENT_MONTH_OPTIONS,
16
16
  } from "./constants/index.js";
17
+ import { GeocodableMixin } from "./mixins/GeocodableMixin.js";
17
18
 
18
19
  const classProps = {
19
20
  code: defField("code", { label: "取引先コード" }),
@@ -82,7 +83,7 @@ const classProps = {
82
83
  * @param {Date} baseDate - base date in UTC (JST - 9 hours)
83
84
  * @returns {Date} payment due date in UTC (JST - 9 hours)
84
85
  *****************************************************************************/
85
- export default class Customer extends FireModel {
86
+ export default class Customer extends GeocodableMixin(FireModel) {
86
87
  static className = "取引先";
87
88
  static collectionPath = "Customers";
88
89
  static useAutonumber = false;
package/src/Employee.js CHANGED
@@ -9,6 +9,7 @@ import { defAccessor } from "./parts/accessorDefinitions.js";
9
9
  import { VALUES as EMPLOYMENT_STATUS_VALUES } from "./constants/employment-status.js";
10
10
  import { VALUES as BLOOD_TYPE_VALUES } from "./constants/blood-type.js";
11
11
  import Certification from "./Certification.js";
12
+ import { GeocodableMixin } from "./mixins/GeocodableMixin.js";
12
13
 
13
14
  const classProps = {
14
15
  code: defField("code", { label: "従業員コード" }),
@@ -213,7 +214,7 @@ const classProps = {
213
214
  *
214
215
  * @function toTerminated - Change the current employee instance to terminated status.
215
216
  *****************************************************************************/
216
- export default class Employee extends FireModel {
217
+ export default class Employee extends GeocodableMixin(FireModel) {
217
218
  static className = "従業員";
218
219
  static collectionPath = "Employees";
219
220
  static useAutonumber = false;
package/src/Site.js CHANGED
@@ -18,6 +18,7 @@ import { defAccessor } from "./parts/accessorDefinitions.js";
18
18
  import Customer from "./Customer.js";
19
19
  import Agreement from "./Agreement.js";
20
20
  import { VALUES } from "./constants/site-status.js";
21
+ import { GeocodableMixin } from "./mixins/GeocodableMixin.js";
21
22
 
22
23
  const classProps = {
23
24
  customerId: defField("customerId", {
@@ -99,7 +100,7 @@ const classProps = {
99
100
  * @param {Agreement} agreement - Agreement instance to remove.
100
101
  * @throws {Error} If agreement not found.
101
102
  *****************************************************************************/
102
- export default class Site extends FireModel {
103
+ export default class Site extends GeocodableMixin(FireModel) {
103
104
  static className = "現場";
104
105
  static collectionPath = "Sites";
105
106
  static useAutonumber = false;
@@ -0,0 +1,149 @@
1
+ /**
2
+ * @file src/mixins/GeocodableMixin.js
3
+ * @description Geocoding 機能を提供する Mixin
4
+ */
5
+
6
+ /**
7
+ * Geocoding 機能を提供する Mixin
8
+ * @param {Class} BaseClass - 継承元のクラス
9
+ * @returns {Class} Geocoding 機能を持つクラス
10
+ */
11
+ export function GeocodableMixin(BaseClass) {
12
+ return class extends BaseClass {
13
+ /**
14
+ * Firestore 用のコンバーターを提供します。
15
+ * - location から geopoint を自動生成して Firestore に保存します。
16
+ * - toObject() は純粋なプレーンオブジェクトを返すため、コンポーネント側で安全に使用できます。
17
+ * @override
18
+ */
19
+ static converter() {
20
+ const superConverter = super.converter();
21
+ return {
22
+ toFirestore: (instance) => {
23
+ const obj = superConverter.toFirestore(instance);
24
+
25
+ // location から geopoint を生成
26
+ if (obj.location?.lat && obj.location?.lng) {
27
+ try {
28
+ const adapter = this.getAdapter();
29
+ const GeoPoint = adapter.GeoPoint;
30
+
31
+ if (GeoPoint) {
32
+ obj.geopoint = new GeoPoint(obj.location.lat, obj.location.lng);
33
+ }
34
+ } catch (error) {
35
+ console.warn(
36
+ `[${this.className}.converter] GeoPoint generation failed:`,
37
+ error
38
+ );
39
+ obj.geopoint = null;
40
+ }
41
+ } else {
42
+ obj.geopoint = null;
43
+ }
44
+
45
+ return obj;
46
+ },
47
+ fromFirestore: superConverter.fromFirestore,
48
+ };
49
+ }
50
+
51
+ /**
52
+ * 新しいドキュメントが作成される前に `location` を取得してセットします。
53
+ * @param {Object} args - Creation options.
54
+ * @param {boolean} [args.skipGeocoding=false] - Skip geocoding process.
55
+ * @returns {Promise<void>}
56
+ */
57
+ async beforeCreate(args = {}) {
58
+ await super.beforeCreate(args);
59
+
60
+ if (!args.skipGeocoding) {
61
+ await this._geocodeAndSetLocation("beforeCreate");
62
+ }
63
+ }
64
+
65
+ /**
66
+ * ドキュメントが更新される前に `location` を取得してセットします。
67
+ * 住所に変更がない場合はジオコーディングをスキップします。
68
+ * @param {Object} args - Update options.
69
+ * @param {boolean} [args.skipGeocoding=false] - Skip geocoding process.
70
+ * @returns {Promise<void>}
71
+ */
72
+ async beforeUpdate(args = {}) {
73
+ await super.beforeUpdate(args);
74
+
75
+ const currentFullAddress = this.fullAddress;
76
+ const previousFullAddress = this._beforeData?.fullAddress;
77
+
78
+ // 住所に変更がない場合、またはskipGeocodingがtrueの場合はスキップ
79
+ if (
80
+ args.skipGeocoding ||
81
+ (this._beforeData && currentFullAddress === previousFullAddress)
82
+ ) {
83
+ return;
84
+ }
85
+
86
+ await this._geocodeAndSetLocation("beforeUpdate");
87
+ }
88
+
89
+ /**
90
+ * ジオコーディング処理
91
+ * fullAddress から緯度・経度・正規化された住所を取得し、location に設定
92
+ * @private
93
+ * @param {string} context - 呼び出し元のコンテキスト('beforeCreate' | 'beforeUpdate')
94
+ */
95
+ async _geocodeAndSetLocation(context) {
96
+ const address = this.fullAddress;
97
+
98
+ if (!address || address.trim() === "") {
99
+ this.location = null;
100
+ return;
101
+ }
102
+
103
+ if (!GeocodableMixin._geocodingFunction) {
104
+ console.warn(
105
+ `[${this.constructor.className}.${context}] Geocoding function not set. Skipping geocoding.`
106
+ );
107
+ this.location = null;
108
+ return;
109
+ }
110
+
111
+ try {
112
+ const coordinates = await GeocodableMixin._geocodingFunction(address);
113
+
114
+ if (coordinates && coordinates.lat && coordinates.lng) {
115
+ this.location = {
116
+ formattedAddress: coordinates.formattedAddress || address,
117
+ lat: coordinates.lat,
118
+ lng: coordinates.lng,
119
+ };
120
+ } else {
121
+ this.location = null;
122
+ }
123
+ } catch (error) {
124
+ console.warn(
125
+ `[${this.constructor.className}.${context}] Geocoding failed:`,
126
+ error
127
+ );
128
+ this.location = null;
129
+ }
130
+ }
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Geocoding 処理を行う関数(外部から注入)
136
+ * @type {Function|null}
137
+ * @static
138
+ */
139
+ GeocodableMixin._geocodingFunction = null;
140
+
141
+ /**
142
+ * Geocoding 関数を設定します。
143
+ * すべての GeocodableMixin を継承したクラスで共有されます。
144
+ * @param {Function} fn - 住所文字列を受け取り、{ lat, lng, formattedAddress } を返す非同期関数
145
+ * @static
146
+ */
147
+ GeocodableMixin.setGeocodingFunction = function (fn) {
148
+ GeocodableMixin._geocodingFunction = fn;
149
+ };