@unifiedcommerce/plugin-uom 0.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.
@@ -0,0 +1,91 @@
1
+ import { eq, and } from "drizzle-orm";
2
+ import { Ok, Err } from "@unifiedcommerce/core";
3
+ import { unitsOfMeasure, uomConversions, entityUom } from "../schema";
4
+ export class UOMService {
5
+ db;
6
+ constructor(db) {
7
+ this.db = db;
8
+ }
9
+ async createUnit(orgId, input) {
10
+ const existing = await this.db.select().from(unitsOfMeasure)
11
+ .where(and(eq(unitsOfMeasure.organizationId, orgId), eq(unitsOfMeasure.code, input.code)));
12
+ if (existing.length > 0)
13
+ return Err(`Unit '${input.code}' already exists`);
14
+ const rows = await this.db.insert(unitsOfMeasure).values({
15
+ organizationId: orgId, code: input.code, name: input.name,
16
+ category: input.category, isBaseUnit: input.isBaseUnit ?? false,
17
+ }).returning();
18
+ return Ok(rows[0]);
19
+ }
20
+ async listUnits(orgId, category) {
21
+ const conditions = [eq(unitsOfMeasure.organizationId, orgId)];
22
+ if (category)
23
+ conditions.push(eq(unitsOfMeasure.category, category));
24
+ const rows = await this.db.select().from(unitsOfMeasure).where(and(...conditions));
25
+ return Ok(rows);
26
+ }
27
+ async createConversion(orgId, input) {
28
+ if (input.factor <= 0)
29
+ return Err("Factor must be positive");
30
+ const rows = await this.db.insert(uomConversions).values({
31
+ organizationId: orgId, fromUnitId: input.fromUnitId,
32
+ toUnitId: input.toUnitId, factor: input.factor,
33
+ }).returning();
34
+ return Ok(rows[0]);
35
+ }
36
+ async listConversions(orgId) {
37
+ const rows = await this.db.select().from(uomConversions)
38
+ .where(eq(uomConversions.organizationId, orgId));
39
+ return Ok(rows);
40
+ }
41
+ async convert(orgId, input) {
42
+ if (input.fromUnitId === input.toUnitId) {
43
+ const unit = await this.db.select().from(unitsOfMeasure).where(eq(unitsOfMeasure.id, input.fromUnitId));
44
+ return Ok({ result: input.quantity, fromCode: unit[0]?.code ?? "", toCode: unit[0]?.code ?? "" });
45
+ }
46
+ // Try forward conversion
47
+ const forward = await this.db.select().from(uomConversions).where(and(eq(uomConversions.organizationId, orgId), eq(uomConversions.fromUnitId, input.fromUnitId), eq(uomConversions.toUnitId, input.toUnitId)));
48
+ const fromUnit = await this.db.select().from(unitsOfMeasure).where(eq(unitsOfMeasure.id, input.fromUnitId));
49
+ const toUnit = await this.db.select().from(unitsOfMeasure).where(eq(unitsOfMeasure.id, input.toUnitId));
50
+ const fromCode = fromUnit[0]?.code ?? "";
51
+ const toCode = toUnit[0]?.code ?? "";
52
+ if (forward.length > 0) {
53
+ return Ok({ result: Math.round(input.quantity * forward[0].factor / 10000), fromCode, toCode });
54
+ }
55
+ // Try reverse
56
+ const reverse = await this.db.select().from(uomConversions).where(and(eq(uomConversions.organizationId, orgId), eq(uomConversions.fromUnitId, input.toUnitId), eq(uomConversions.toUnitId, input.fromUnitId)));
57
+ if (reverse.length > 0) {
58
+ return Ok({ result: Math.round(input.quantity * 10000 / reverse[0].factor), fromCode, toCode });
59
+ }
60
+ return Err(`No conversion found between '${fromCode}' and '${toCode}'`);
61
+ }
62
+ async setEntityUom(orgId, input) {
63
+ const existing = await this.db.select().from(entityUom)
64
+ .where(and(eq(entityUom.organizationId, orgId), eq(entityUom.entityId, input.entityId)));
65
+ if (existing.length > 0) {
66
+ const rows = await this.db.update(entityUom).set({
67
+ purchaseUomId: input.purchaseUomId, stockUomId: input.stockUomId,
68
+ saleUomId: input.saleUomId, yieldPercentage: input.yieldPercentage ?? 100, updatedAt: new Date(),
69
+ }).where(eq(entityUom.id, existing[0].id)).returning();
70
+ return Ok(rows[0]);
71
+ }
72
+ const rows = await this.db.insert(entityUom).values({
73
+ organizationId: orgId, entityId: input.entityId,
74
+ purchaseUomId: input.purchaseUomId, stockUomId: input.stockUomId,
75
+ saleUomId: input.saleUomId, yieldPercentage: input.yieldPercentage ?? 100,
76
+ }).returning();
77
+ return Ok(rows[0]);
78
+ }
79
+ async getEntityUom(orgId, entityId) {
80
+ const rows = await this.db.select().from(entityUom)
81
+ .where(and(eq(entityUom.organizationId, orgId), eq(entityUom.entityId, entityId)));
82
+ if (rows.length === 0)
83
+ return Err("No UOM assignment for this entity");
84
+ return Ok(rows[0]);
85
+ }
86
+ async calculateYield(yieldPercentage, requiredQuantity) {
87
+ if (yieldPercentage <= 0 || yieldPercentage > 100)
88
+ return Ok({ purchaseQuantity: requiredQuantity });
89
+ return Ok({ purchaseQuantity: Math.ceil(requiredQuantity * 100 / yieldPercentage) });
90
+ }
91
+ }