@mj-biz-apps/sales-entities 5.2.0 → 6.1.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/dist/product-filter.d.ts
CHANGED
|
@@ -17,16 +17,25 @@
|
|
|
17
17
|
* at all. So a name- or SKU-matching resolver can be a SUGGESTION a human confirms, never an identity
|
|
18
18
|
* mechanism, and `DealLine.ProductID` stores the ID.
|
|
19
19
|
*
|
|
20
|
-
* ── THE
|
|
20
|
+
* ── THE CONDITIONS ────────────────────────────────────────────────────────────────────────
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* There used to be a THIRD condition, and removing it was a business decision rather than a tidy-up.
|
|
23
|
+
*
|
|
24
|
+
* The filter began with `CompanyID = <the deal's company>`, on the reasoning that products are
|
|
25
|
+
* per-company and a rep on one company's pipeline selecting another's product would be a data leak.
|
|
26
|
+
* That reading was wrong for Blue Cypress: every deal is a Blue Cypress deal, and company ownership
|
|
27
|
+
* lives at the PRODUCT rather than at the deal (Johanna Snider, Sales channel, 2026-08-26; issue #29).
|
|
28
|
+
* With both pipelines owned by Blue Cypress the clause made every non-Blue-Cypress product unsellable,
|
|
29
|
+
* so an Account Director could not put a Betty or Sidecar product on a deal at all.
|
|
30
|
+
*
|
|
31
|
+
* It is not a tenancy boundary being relaxed, because there was never one here to relax. `DECISIONS.md`
|
|
32
|
+
* D5 already says a deal lives in ONE company's pipeline while its lines carry their OWN company, taken
|
|
33
|
+
* from the product. The clause contradicted D5; the picker now agrees with it.
|
|
34
|
+
*
|
|
35
|
+
* 1. **Status = 'Active'.** `Draft` is not sellable yet; `Discontinued` and `EOL` are not sellable any
|
|
27
36
|
* more. Compared as a literal because it is orders' own CHECK-constrained vocabulary, not this app's
|
|
28
37
|
* — the Sales vocabulary rule governs Sales' type tables, and this column belongs to another app.
|
|
29
|
-
*
|
|
38
|
+
* 2. **The availability window.** `AvailableFrom` / `AvailableTo` are nullable and open-ended on either
|
|
30
39
|
* side, so the test is "not yet started" and "already ended" rather than a range containment. Both
|
|
31
40
|
* NULL means always available, which is the common case.
|
|
32
41
|
*
|
|
@@ -51,6 +60,28 @@ export interface ProductLookup {
|
|
|
51
60
|
ID: string;
|
|
52
61
|
Name: string;
|
|
53
62
|
SKU: string | null;
|
|
63
|
+
/**
|
|
64
|
+
* The company that OWNS this product, and so the company its revenue books to.
|
|
65
|
+
*
|
|
66
|
+
* Carried because the picker no longer filters on company, which means a line's company can no
|
|
67
|
+
* longer be inferred from the deal — it has to come from the product the rep actually chose.
|
|
68
|
+
* `OnProductChange` stamps it. Orders' `OrderLineEntityServer` overwrites it from the product at
|
|
69
|
+
* save regardless; this makes the BROWSER agree with that rather than guess, which matters because
|
|
70
|
+
* `deal.Validate()` runs client-side where that server subclass does not exist.
|
|
71
|
+
*/
|
|
72
|
+
CompanyID: string;
|
|
73
|
+
/**
|
|
74
|
+
* The owning company's NAME, for display only — never for matching.
|
|
75
|
+
*
|
|
76
|
+
* The picker spans every company since #29, so `Name` alone no longer identifies a product to a
|
|
77
|
+
* reader. Two companies can each sell an "Onboarding Fee", and `SKU` is nullable with only a
|
|
78
|
+
* FILTERED unique index, so neither field disambiguates them. Without the company on the label a
|
|
79
|
+
* rep chooses between two identical-looking rows, and the choice decides which company's books the
|
|
80
|
+
* revenue lands in.
|
|
81
|
+
*
|
|
82
|
+
* It is a virtual field on orders' Products view, so it costs nothing but a column.
|
|
83
|
+
*/
|
|
84
|
+
Company: string | null;
|
|
54
85
|
}
|
|
55
86
|
/**
|
|
56
87
|
* The filter that decides what a rep may select, as of `asOf`.
|
|
@@ -59,8 +90,7 @@ export interface ProductLookup {
|
|
|
59
90
|
* the comment explaining each. The date is passed in rather than read from the clock inside so the
|
|
60
91
|
* behaviour is testable at a chosen instant.
|
|
61
92
|
*
|
|
62
|
-
* @param companyID - The selling company, from the deal's pipeline.
|
|
63
93
|
* @param asOf - The date the availability window is judged against. UTC, because everything stored is.
|
|
64
94
|
*/
|
|
65
|
-
export declare function ProductFilterFor(
|
|
95
|
+
export declare function ProductFilterFor(asOf: Date): string;
|
|
66
96
|
//# sourceMappingURL=product-filter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-filter.d.ts","sourceRoot":"","sources":["../src/product-filter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"product-filter.d.ts","sourceRoot":"","sources":["../src/product-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,gCAAgC,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB;;;;;;;;OAQG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;;;;;OAUG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAOnD"}
|
package/dist/product-filter.js
CHANGED
|
@@ -17,16 +17,25 @@
|
|
|
17
17
|
* at all. So a name- or SKU-matching resolver can be a SUGGESTION a human confirms, never an identity
|
|
18
18
|
* mechanism, and `DealLine.ProductID` stores the ID.
|
|
19
19
|
*
|
|
20
|
-
* ── THE
|
|
20
|
+
* ── THE CONDITIONS ────────────────────────────────────────────────────────────────────────
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* There used to be a THIRD condition, and removing it was a business decision rather than a tidy-up.
|
|
23
|
+
*
|
|
24
|
+
* The filter began with `CompanyID = <the deal's company>`, on the reasoning that products are
|
|
25
|
+
* per-company and a rep on one company's pipeline selecting another's product would be a data leak.
|
|
26
|
+
* That reading was wrong for Blue Cypress: every deal is a Blue Cypress deal, and company ownership
|
|
27
|
+
* lives at the PRODUCT rather than at the deal (Johanna Snider, Sales channel, 2026-08-26; issue #29).
|
|
28
|
+
* With both pipelines owned by Blue Cypress the clause made every non-Blue-Cypress product unsellable,
|
|
29
|
+
* so an Account Director could not put a Betty or Sidecar product on a deal at all.
|
|
30
|
+
*
|
|
31
|
+
* It is not a tenancy boundary being relaxed, because there was never one here to relax. `DECISIONS.md`
|
|
32
|
+
* D5 already says a deal lives in ONE company's pipeline while its lines carry their OWN company, taken
|
|
33
|
+
* from the product. The clause contradicted D5; the picker now agrees with it.
|
|
34
|
+
*
|
|
35
|
+
* 1. **Status = 'Active'.** `Draft` is not sellable yet; `Discontinued` and `EOL` are not sellable any
|
|
27
36
|
* more. Compared as a literal because it is orders' own CHECK-constrained vocabulary, not this app's
|
|
28
37
|
* — the Sales vocabulary rule governs Sales' type tables, and this column belongs to another app.
|
|
29
|
-
*
|
|
38
|
+
* 2. **The availability window.** `AvailableFrom` / `AvailableTo` are nullable and open-ended on either
|
|
30
39
|
* side, so the test is "not yet started" and "already ended" rather than a range containment. Both
|
|
31
40
|
* NULL means always available, which is the common case.
|
|
32
41
|
*
|
|
@@ -48,15 +57,11 @@ export const E_ORDERS_PRODUCT = 'MJ_BizApps_Orders: Products';
|
|
|
48
57
|
* the comment explaining each. The date is passed in rather than read from the clock inside so the
|
|
49
58
|
* behaviour is testable at a chosen instant.
|
|
50
59
|
*
|
|
51
|
-
* @param companyID - The selling company, from the deal's pipeline.
|
|
52
60
|
* @param asOf - The date the availability window is judged against. UTC, because everything stored is.
|
|
53
61
|
*/
|
|
54
|
-
export function ProductFilterFor(
|
|
62
|
+
export function ProductFilterFor(asOf) {
|
|
55
63
|
const day = `${asOf.getUTCFullYear()}-${String(asOf.getUTCMonth() + 1).padStart(2, '0')}-${String(asOf.getUTCDate()).padStart(2, '0')}`;
|
|
56
|
-
|
|
57
|
-
// the filter is still concatenated SQL and the habit is what keeps it safe when a caller changes.
|
|
58
|
-
const company = companyID.replace(/'/g, "''");
|
|
59
|
-
return (`CompanyID = '${company}' AND Status = 'Active' ` +
|
|
64
|
+
return (`Status = 'Active' ` +
|
|
60
65
|
`AND (AvailableFrom IS NULL OR AvailableFrom <= '${day}') ` +
|
|
61
66
|
`AND (AvailableTo IS NULL OR AvailableTo >= '${day}')`);
|
|
62
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-filter.js","sourceRoot":"","sources":["../src/product-filter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"product-filter.js","sourceRoot":"","sources":["../src/product-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,6FAA6F;AAC7F,MAAM,CAAC,MAAM,gBAAgB,GAAG,6BAA6B,CAAC;AAqC9D;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAU;IACvC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,cAAc,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IACxI,OAAO,CACH,oBAAoB;QACpB,mDAAmD,GAAG,KAAK;QAC3D,+CAA+C,GAAG,IAAI,CACzD,CAAC;AACN,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mj-biz-apps/sales-entities",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.1.0",
|
|
5
5
|
"description": "MJ BizApps Sales Entity Subclasses",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
"typescript": "^5.9.3"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@mj-biz-apps/orders-entities": "^5.
|
|
21
|
+
"@mj-biz-apps/orders-entities": "^5.2.1",
|
|
22
22
|
"zod": "~3.24.4"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@memberjunction/core": "^6.1.0-edge.
|
|
26
|
-
"@memberjunction/global": "^6.1.0-edge.
|
|
25
|
+
"@memberjunction/core": "^6.1.0-edge.5",
|
|
26
|
+
"@memberjunction/global": "^6.1.0-edge.5"
|
|
27
27
|
},
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|