@leaddreamer/inventorylogic 0.0.3-beta.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.
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Typedefs, constants, support functions and database operations related to Commodity Matrials
3
+ * users
4
+ * @module Commodities
5
+ */
6
+ import {
7
+ writeRecord,
8
+ writeBack,
9
+ updateRecord,
10
+ fetchRecord,
11
+ collectRecords,
12
+ deleteRecord,
13
+ PaginateFetch,
14
+ } from "@leaddreamer/firebase-wrapper";
15
+ import { Forms, fillForm } from "@leaddreamer/staticforms";
16
+
17
+ import { UNITS, MATERIAL, COMMODITIES, COMMODITY, COMPONENT_RANGE } from "../boilerplate";
18
+
19
+ //////////////////////////////////////////////////////////////////////
20
+ // Constants
21
+
22
+ /**
23
+ * a record
24
+ * @typedef {object} Commodity
25
+ * @property {?string} Id Id of document (redundant for convenience)
26
+ * @property {?string} refPath if from database, full path to document
27
+ * @property {!string} description description, e.g. "Wire, Silver, Sterling, 20ga"
28
+ * @property {!string} unit usage/measurement unit (for unit conversion)
29
+ * @property {!number} rate the per unit cost of this commodity
30
+ * @property {string} MATERIAL "base", "highValue" or other value tp be defined
31
+ * @property {?url} image a reference image for the commodity
32
+ */
33
+
34
+ /**
35
+ * @readonly
36
+ * @constant
37
+ * @type {Commodity}
38
+ * @default
39
+ */
40
+ const CommodityDefault = {
41
+ Id: null,
42
+ refPath: null,
43
+ description: null,
44
+ unit: null,
45
+ rate: 0,
46
+ [MATERIAL]: null,
47
+ image: null,
48
+ };
49
+
50
+ /**
51
+ * @readonly
52
+ * @constant
53
+ * @type {Commodity}
54
+ * @default
55
+ */
56
+ const CommodityData = {
57
+ Id: `string.uuid`,
58
+ refPath: null,
59
+ description: `commerce.productDescription`,
60
+ unit: `lookup:${UNITS}`,
61
+ rate: `commerce.price:${COMPONENT_RANGE}`,
62
+ [MATERIAL]: `lookup:${MATERIAL}`,
63
+ image: null,
64
+ };
65
+
66
+ /**
67
+ * @readonly
68
+ * @constant
69
+ * @type {Commodity}
70
+ * @default
71
+ */
72
+ const CommodityDefaultForm = {
73
+ description: "text",
74
+ unit: `lookup:${UNITS}`,
75
+ rate: "number",
76
+ [MATERIAL]:`lookup:${MATERIAL}`,
77
+ image: "url",
78
+ };
79
+
80
+ Forms.Base[COMMODITIES] = CommodityDefault;
81
+ Forms.BaseForm[COMMODITIES] = CommodityDefaultForm;
82
+ Forms.CreateForm[COMMODITIES] = CommodityDefaultForm;
83
+ Forms.Data[COMMODITIES] = CommodityData;
84
+ Forms.Summarize[COMMODITIES] = summarizeCommodity;
85
+ Forms.Save[COMMODITIES] = updateCommodity;
86
+ Forms.SearchBy[COMMODITIES] = "description";
87
+
88
+ //////////////////////////////////////////////////////////////////////
89
+ // record operations
90
+ /**
91
+ * @param {CommodityDefault} commodity
92
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
93
+ * @returns {Promise<void>}
94
+ */
95
+ export async function deleteCommodity(commodity, user = null) {
96
+ return deleteRecord(commodity, COMMODITY, user?.refPath);
97
+ }
98
+
99
+ /**
100
+ * @param {CommodityDefault} commodity minimal record of user account
101
+ * @param {!string} commodity.Id record Id of account
102
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
103
+ * @returns {Promise<void>}
104
+ */
105
+ export async function deleteCommodityById(commodity, user = null) {
106
+ if (!commodity?.Id) return;
107
+
108
+ return deleteRecord(commodity, COMMODITY, user?.refPath);
109
+ }
110
+
111
+ /**
112
+ * @param {Commodity} commodity minimal record of user account
113
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
114
+ * @returns {Promise.Commodity}
115
+ */
116
+ export const createCommodity = (commodity, user = null) => {
117
+ //need a "no duplicate" function - whatever that means in this context
118
+ return writeRecord(COMMODITY, commodity, user?.refPath);
119
+ };
120
+
121
+ /**
122
+ * writes record back to database. Assumes only Id is valid
123
+ * @param {Commodity} commodity a partially-populated commodity record for updates
124
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
125
+ * @returns {promise.Commodity} a copy of the data written
126
+ */
127
+ export async function updateCommodityByID(commodity, parent = null) {
128
+ // will update just the fields provided
129
+ return writeBack({
130
+ ...commodity,
131
+ refPath: `${parent?.refPath}/${COMMODITY}/${commodity.Id}`,
132
+ });
133
+ }
134
+
135
+ /**
136
+ * writes record back to database. Assumes only Id is valid
137
+ * @param {Commodity} commodity a partially-populated commodity record for updates
138
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
139
+ * @returns {promise.Commodity} a copy of the data written
140
+ */
141
+ export async function updateCommodity(commodity, user = null) {
142
+
143
+ return updateRecord(
144
+ fillForm(commodity, COMMODITY), //saves only APPROPRIATE fields -does NOT populate missing fields
145
+ user,
146
+ COMMODITY
147
+ );
148
+ }
149
+
150
+ //////////////////////////////////////////////////////////////////////
151
+ // fetches
152
+
153
+ /**
154
+ * Checks if this is a known Commodity Id
155
+ * @param {!string} Id Id value to check
156
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
157
+ * @returns {Promise.boolean} true is this Id is in database
158
+ */
159
+ export async function knownCommodity(Id, user = null) {
160
+ let exists = true;
161
+ try {
162
+ await fetchCommodity(Id);
163
+ } catch (err) {
164
+ exists = false;
165
+ }
166
+ return exists;
167
+ }
168
+
169
+ //////////////////////////////////////////////////////////////////////
170
+ // fetches
171
+
172
+ /**
173
+ * Fetches ALL Commodity records. Not very useful
174
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
175
+ * @returns {Array.Commodity}
176
+ */
177
+ export async function fetchCommodities(user = null) {
178
+ return collectRecords(COMMODITY, user?.refPath);
179
+ }
180
+
181
+ /**
182
+ * An object to allow for paginating a table read from Firestore. REQUIRES a sorting choice
183
+ * @typedef {object} PaginatedFetch
184
+ * @property {Query} Query that forms basis for the table read
185
+ * @property {number} limit page size
186
+ * @property {QuerySnapshot} snapshot last successful snapshot/page fetched
187
+ * @property {enum} status status of pagination object
188
+ * @property {method} PageForward pages the fetch forward
189
+ * @property {method} PageBack pages the fetch backward
190
+
191
+ */
192
+
193
+ /**
194
+ * Fetchs an object to fetch pages of commodity data, ascending by name, "limit" at a time
195
+ * @param {number} limit
196
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
197
+ * @returns {PaginatedFetch} object with pagination methods
198
+ */
199
+
200
+ export async function fetchCommoditiesPaginated(limit, user = null) {
201
+ return new PaginateFetch(
202
+ COMMODITY,
203
+ null, //filter
204
+ [{ fieldRef: "description", dirStr: "asc" }], //sort
205
+ user?.refPath, //ref
206
+ limit,
207
+ );
208
+ }
209
+
210
+ /**
211
+ * Fetches a single Commodity record by Id
212
+ * @param {!string} commodityId
213
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
214
+ * @returns {Promise.Commodity}
215
+ */
216
+ export async function fetchCommodity(commodityId, user = null) {
217
+ return fetchRecord(COMMODITY, commodityId, user?.refPath);
218
+ }
219
+
220
+ /**
221
+ * Fetches and returns the summarized name from a Commodity record
222
+ * @param {!string} commodityId
223
+ * @param {UserObject} user minimal record of user this commodity belongs to (if any)
224
+ * @returns {Promise.string} the summarized name from the record
225
+ */
226
+ export async function fetchCommodityName(commodityId, user = null) {
227
+ const commodity = await fetchCommodity(commodityId, user);
228
+ return summarizeCommodity(commodity);
229
+ }
230
+
231
+ //////////////////////////////////////////////////////////////////////
232
+ // utilities
233
+ /**
234
+ * Returns a standardized formatted name string
235
+ * @param {Commodity}
236
+ * @returns {string} formatted name string
237
+ */
238
+ function summarizeCommodity(commodity) {
239
+ return `${commodity?.description}`;
240
+ }
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Typedefs, constants, support functions and database operations related to LineItem Matrials
3
+ * users
4
+ * @module LineItem
5
+ */
6
+ import {
7
+ writeRecord,
8
+ updateRecord,
9
+ fetchRecord,
10
+ collectRecordsByFilter,
11
+ deleteRecord,
12
+ PaginateFetch,
13
+ } from "@leaddreamer/firebase-wrapper";
14
+ import { Forms, fillForm } from "@leaddreamer/staticforms";
15
+
16
+ import {
17
+ COMMODITY,
18
+ LINEITEM,
19
+ STAGE,
20
+ MATERIAL,
21
+ UNITS,
22
+ COMPONENT_RANGE,
23
+ COMPONENT_COUNT_RANGE,
24
+ } from "../boilerplate";
25
+ import {StageLevels, MaterialLevels} from "../Markups";
26
+
27
+ //////////////////////////////////////////////////////////////////////
28
+ // Constants
29
+
30
+ /**
31
+ * a record
32
+ * @typedef {object} LineItem
33
+ * @property {string} Id Id of document (redundant for convenience)
34
+ * @property {string} refPath if from database, full path to document
35
+ * @property {StageLevels} STAGE "fabrication", "wholesale", "retail" or other to be defined
36
+ * @property {MaterialLevels} MATERIAL "base", "highValue", "service" or other value to be defined
37
+ * @property {null|string} commodity Id of a commodity item/material (optional)
38
+ * @property {string|null} description description, e.g. "Wire, Silver, Sterling, 20ga"
39
+ * //can be overwritten by commodity
40
+ * @property {string|null} unit usage/measurement unit (for unit conversion)
41
+ * //can be overwritten by commodity
42
+ * @property {number} rate the per unit cost of this lineItem
43
+ * //can be overwritten by commodity
44
+ * @property {number} amount amount of material/item useed
45
+ * @property {datetime} timestamp initial time of creation for sorting
46
+ * @property {url|null} image a reference image for the lineItem
47
+ */
48
+ /**
49
+ * Default/Blank Form for LineItem data records
50
+ * @readonly
51
+ * @constant
52
+ * @type {LineItem}
53
+ * @default
54
+ */
55
+ const LineItemDefault = {
56
+ Id: null,
57
+ refPath: null,
58
+ [STAGE]: null,
59
+ [MATERIAL]: null,
60
+ commodity: null,
61
+ description: null,
62
+ unit: null,
63
+ rate: 0,
64
+ count: 0,
65
+ image: null,
66
+ };
67
+
68
+ /**
69
+ * Form for generating placeholder LineItem data.
70
+ * @readonly
71
+ * @constant
72
+ * @type {LineItem}
73
+ * @default
74
+ */
75
+ const LineItemData = {
76
+ Id: `string.uuid`,
77
+ refPath: null,
78
+ [STAGE]: `lookupChoices:${STAGE}`,
79
+ [MATERIAL]: `lookupChoices:${MATERIAL}`,
80
+ commodity: `lookupLevels:${COMMODITY}`,
81
+ description: `commerce.productDescription`,
82
+ count: `number.int:${COMPONENT_COUNT_RANGE}`,
83
+ unit: `lookupLink:units`,
84
+ rate: `commerce.price:${COMPONENT_RANGE}`,
85
+ line_total: `commerce.price:${COMPONENT_RANGE}`,
86
+ image: null,
87
+ };
88
+
89
+ /**
90
+ * Form for driving an Editor form for LineItem Data
91
+ * @readonly
92
+ * @constant
93
+ * @type {LineItem}
94
+ * @default
95
+ */
96
+ const LineItemDefaultForm = {
97
+ [STAGE]: `lookupChoices:${STAGE}`,
98
+ [MATERIAL]: `lookupChoices:${MATERIAL}`,
99
+ commodity: `lookupLevels:${COMMODITY}`,
100
+ description: `text`,
101
+ count: `number`,
102
+ unit: `lookupLink:${UNITS}`,
103
+ rate: `number`,
104
+ line_total: `lineSum`,
105
+ image: `url`,
106
+ };
107
+
108
+ Forms.Base[LINEITEM] = LineItemDefault;
109
+ Forms.BaseForm[LINEITEM] = LineItemDefaultForm;
110
+ Forms.CreateForm[LINEITEM] = LineItemDefaultForm;
111
+ Forms.Data[LINEITEM] = LineItemData;
112
+ Forms.Summarize[LINEITEM] = summarizeLineItem;
113
+ Forms.Save[LINEITEM] = updateLineItem;
114
+ Forms.SearchBy[LINEITEM] = "description";
115
+ Forms.LineSum[LINEITEM] = lineItem_lineSum; //FUNCTION
116
+ Forms.UpdateChange[LINEITEM] = UpdateChange;
117
+
118
+
119
+ //////////////////////////////////////////////////////////////////////
120
+ // record operations
121
+ /**
122
+ * deletes a lineItem. Product required if lineItem is missing refPath
123
+ * from database
124
+ * @param {LineItemDefault} lineItem lineItem record
125
+ * @param {!string} lineItem.Id at least Id is required to identify record
126
+ * @param {string|null} lineItem.refPath refpath will be used if present
127
+ * @param {Product|null} product
128
+ * @returns {Promise<void>}
129
+ */
130
+ export async function deleteLineItem(lineItem, product = null) {
131
+ return deleteRecord(lineItem, LINEITEM, product?.refPath);
132
+ }
133
+
134
+ /**
135
+ * @param {LineItemDefault} lineItem minimal record of lineItem in question
136
+ * @param {!string} lineItem.Id record Id of account
137
+ * @param {Product} product minimal record of product this lineItem belongs to
138
+ * @param {!string} product.refPath required as a minimum
139
+ * @returns {Promise<void>}
140
+ */
141
+ export async function deleteLineItemById(lineItem, product) {
142
+ if (!lineItem?.Id) return;
143
+
144
+ return deleteRecord(lineItem, LINEITEM, product?.refPath);
145
+ }
146
+
147
+ /**
148
+ * @param {LineItem} lineItem minimal record of user account
149
+ * @param {Product} product minimal record of product this lineItem belongs to
150
+ * @returns {Promise.LineItem}
151
+ */
152
+ export const createLineItem = (lineItem) => {
153
+ //need a "no duplicate" function - whatever that means in this context
154
+ return writeRecord(LINEITEM, lineItem, product.refPath);
155
+ };
156
+
157
+ /**
158
+ * writes record back to database. Assumes only Id is valid
159
+ * @param {LineItem} lineItem a partially-populated lineItem record for updates
160
+ * @param {Product} product minimal record of product this lineItem belongs to
161
+ * @returns {promise.LineItem} a copy of the data written
162
+ */
163
+ async function updateLineItem(lineItem, product = null) {
164
+ if (!(lineItem?.refPath || product?.refPath)) {
165
+ console.log("no product specified to save LineItem");
166
+ //no root lineItems
167
+ return null;
168
+ };
169
+
170
+
171
+ return updateRecord(
172
+ fillForm(lineItem, LINEITEM), //saves only APPROPRIATE fields -does NOT populate missing fields
173
+ product,
174
+ LINEITEM
175
+ );
176
+ }
177
+
178
+ //////////////////////////////////////////////////////////////////////
179
+ // fetches
180
+
181
+ /**
182
+ * Checks if this is a known LineItem Id
183
+ * @param {!string} Id Id value to check
184
+ * @param {Product} product minimal record of product this lineItem belongs to
185
+ * @returns {Promise.boolean} true is this Id is in database
186
+ */
187
+ export async function knownLineItem(Id, product) {
188
+ let exists = true;
189
+ try {
190
+ await fetchLineItem(Id, product);
191
+ } catch (err) {
192
+ exists = false;
193
+ }
194
+ return exists;
195
+ }
196
+
197
+ //////////////////////////////////////////////////////////////////////
198
+ // fetches
199
+
200
+ /**
201
+ * Fetches ALL LineItem records. Not very useful
202
+ * @returns {Array.LineItem}
203
+ */
204
+ export async function fetchLineItems() {
205
+ return collectRecordsInGroup(LINEITEM);
206
+ }
207
+
208
+ /**
209
+ * Fetches ALL LineItem records for a specific product. Not very useful
210
+ * @param {Product} product minimal record of product this lineItem belongs to
211
+ * @param {?Stage} stage optional stage selection, to get a subset of the line items
212
+ * @returns {Array.LineItem}
213
+ */
214
+ export async function fetchProductLineItems(product, stage = null) {
215
+ const filterArray = stage
216
+ ? [{ fieldRef: "stage", opStr: "==", value: stage.name }]
217
+ : null;
218
+ return collectRecordsByFilter(
219
+ LINEITEM, //table
220
+ product.refPath, //refPath
221
+ filterArray, //filterArray
222
+ null, //sort
223
+ );
224
+ }
225
+
226
+ /**
227
+ * An object to allow for paginating a table read from Firestore. REQUIRES a sorting choice
228
+ * @typedef {object} PaginatedFetch
229
+ * @property {Query} Query that forms basis for the table read
230
+ * @property {number} limit page size
231
+ * @property {QuerySnapshot} snapshot last successful snapshot/page fetched
232
+ * @property {enum} status status of pagination object
233
+ * @property {method} PageForward pages the fetch forward
234
+ * @property {method} PageBack pages the fetch backward
235
+
236
+ */
237
+
238
+ /**
239
+ * Fetchs an object to fetch pages of lineItem data, ascending by name, "limit" at a time
240
+ * NOT a "listener"
241
+ * @param {Product} product
242
+ * @param {?Stage} stage optional stage selection, to get a subset of the line items
243
+ * @param {number} limit
244
+ * @returns {PaginatedFetch} object with pagination methods
245
+ */
246
+
247
+ export async function paginateProductLineItems(
248
+ product,
249
+ stage = null,
250
+ limit = null,
251
+ ) {
252
+ const filterArray = stage
253
+ ? [{ fieldRef: "stage", opStr: "==", value: stage.name }]
254
+ : null;
255
+
256
+ return new PaginateFetch(
257
+ LINEITEM,
258
+ filterArray, //filter
259
+ [{ fieldRef: "timestamp", dirStr: "asc" }], //sort
260
+ product.refPath, //ref
261
+ limit,
262
+ );
263
+ }
264
+
265
+ /**
266
+ * Fetches a single LineItem record by Id
267
+ * @param {!string} lineItemId
268
+ * @param {Product} product minimal record of product this lineItem belongs to
269
+ * @returns {Promise.LineItem}
270
+ */
271
+ export async function fetchLineItem(lineItemId, product) {
272
+ return fetchRecord(LINEITEM, lineItemId, product.refPath);
273
+ }
274
+
275
+ /**
276
+ * Fetches and returns the summarized name from a LineItem record
277
+ * @param {!string} lineItemId
278
+ * @param {Product} product minimal record of product this lineItem belongs to
279
+ * @returns {Promise.string} the summarized name from the record
280
+ */
281
+ export async function fetchLineItemName(lineItemId, product) {
282
+ const lineItem = await fetchLineItem(lineItemId, product);
283
+ return summarizeLineItem(lineItem);
284
+ }
285
+
286
+ //////////////////////////////////////////////////////////////////////
287
+ // utilities
288
+ /**
289
+ * Returns a standardized formatted name string
290
+ * @param {LineItem}
291
+ * @returns {string} formatted name string
292
+ */
293
+ function summarizeLineItem(lineItem) {
294
+ return `${lineItem?.description}`;
295
+ }
296
+
297
+ /**
298
+ * calculates line-sums for display in reactdatagrid tables
299
+ * @param {CustomRenderProps} customRenderProps
300
+ * @returns {number | string} for display
301
+ */
302
+
303
+ /**
304
+ * Calculates the lineSum (total) for a lineItem
305
+ * @param {object} lineItem
306
+ * @param {?number} lineItem.rate
307
+ * @param {?number} lineItem.count
308
+ * @returns {number}
309
+ */
310
+ function lineItem_lineSum(lineItem) {
311
+ return lineItem?.rate * lineItem?.count;
312
+ }
313
+
314
+ /**
315
+ * checks for cross-dependencies - in this case, adjusting the "count" if the UNIT changes
316
+ * @param {object} prev
317
+ * @param {object} value
318
+ * @param {string} itemFor
319
+ * @param {object} fullChoiceObject
320
+ * @returns {object}
321
+ */
322
+ function UpdateChange(prevItem, newValue, itemFor, fullChoiceObject) {
323
+ console.log("LineItem UpdateChange", prevItem, newValue, itemFor, fullChoiceObject);
324
+ return {
325
+ ...prevItem,
326
+ [itemFor]: value
327
+ }
328
+ }