@stacksjs/commerce 0.75.90 → 0.75.92
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/index.d.ts +4 -0
- package/dist/index.js +5 -5
- package/dist/register.d.ts +7 -0
- package/dist/register.js +1 -1
- package/dist/sales-tax.d.ts +73 -0
- package/dist/sales-tax.js +2 -0
- package/dist/tax/breakdown.d.ts +4 -42
- package/package.json +12 -12
package/dist/register.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ export declare function setQuantity(basket: BasketLine[], product: Product, quan
|
|
|
10
10
|
export declare function basketLines<T extends Product>(basket: BasketLine[], products: T[], fields?: RegisterFields): Array<BasketLine & { product: T }>;
|
|
11
11
|
/** The basket's total, in minor units. */
|
|
12
12
|
export declare function basketTotal(basket: BasketLine[], products: Product[], fields?: RegisterFields): number;
|
|
13
|
+
/**
|
|
14
|
+
* The part of the basket's total that sales tax applies to: every line whose
|
|
15
|
+
* product is not marked `taxable: false` (a food product, an admission).
|
|
16
|
+
* Feed it to `breakdownFor` from `@stacksjs/commerce/sales-tax`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function basketTaxable(basket: BasketLine[], products: Product[], fields?: RegisterFields): number;
|
|
13
19
|
/** How many units are in the basket. */
|
|
14
20
|
export declare function basketCount(basket: BasketLine[]): number;
|
|
15
21
|
/**
|
|
@@ -51,6 +57,7 @@ export declare interface RegisterFields {
|
|
|
51
57
|
price: string
|
|
52
58
|
inventory: string
|
|
53
59
|
lowStock: string
|
|
60
|
+
taxable: string
|
|
54
61
|
}
|
|
55
62
|
export declare interface StockLabel { text: string, tone: 'out' | 'low' | 'ok' }
|
|
56
63
|
declare type Product = Record<string, any>;
|
package/dist/register.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var k=Object.defineProperty;var
|
|
2
|
+
var k=Object.defineProperty;var x=(t)=>t;function m(t,n){this[t]=x.bind(null,n)}var b=(t,n)=>{for(var e in n)k(t,e,{get:n[e],enumerable:!0,configurable:!0,set:m.bind(n,e)})};var h=(t,n,e)=>()=>{if(t)try{n=t(t=0)}catch(r){e=[r]}if(e)throw e[0];return n};var S={};b(S,{CENTS_FIELDS:()=>g,DEFAULT_FIELDS:()=>u,addToBasket:()=>L,basketCount:()=>B,basketLines:()=>l,basketTaxable:()=>I,basketTotal:()=>F,canAdd:()=>f,groupsOf:()=>T,setQuantity:()=>y,shelfLeft:()=>a,stockLabel:()=>R});var u={id:"id",price:"price",inventory:"inventory",lowStock:"low_stock_threshold",taxable:"taxable"},g={id:"id",price:"price_cents",inventory:"inventory_quantity",lowStock:"low_stock_threshold",taxable:"taxable"};function s(t,n){return Number(t[n.id])}function p(t,n){let e=t[n.inventory];return e===null||e===void 0||e===""?null:Number(e)}function a(t,n,e=u){let r=p(t,e);if(r===null)return null;let o=n.find((c)=>c.productId===s(t,e))?.quantity||0;return Math.max(0,r-o)}function f(t,n,e=u){let r=a(t,n,e);return r===null||r>0}function L(t,n,e=u){if(!f(n,t,e))return t;let r=s(n,e);return t.some((o)=>o.productId===r)?t.map((o)=>o.productId===r?{...o,quantity:o.quantity+1}:o):[...t,{productId:r,quantity:1}]}function y(t,n,e,r=u){let o=s(n,r),c=p(n,r),d=Math.min(Math.max(0,Math.floor(Number(e)||0)),c===null?Number.POSITIVE_INFINITY:c);if(d===0)return t.filter((i)=>i.productId!==o);return t.some((i)=>i.productId===o)?t.map((i)=>i.productId===o?{...i,quantity:d}:i):[...t,{productId:o,quantity:d}]}function l(t,n,e=u){return t.map((r)=>({...r,product:n.find((o)=>s(o,e)===r.productId)})).filter((r)=>!!r.product)}function F(t,n,e=u){return l(t,n,e).reduce((r,o)=>r+Number(o.product[e.price]||0)*o.quantity,0)}function I(t,n,e=u){return l(t,n,e).filter((r)=>r.product[e.taxable]!==!1).reduce((r,o)=>r+Number(o.product[e.price]||0)*o.quantity,0)}function B(t){return t.reduce((n,e)=>n+e.quantity,0)}function R(t,n=[],e=u,r=5){let o=a(t,n,e);if(o===null)return null;if(o===0)return{text:"Sold out",tone:"out"};let c=t[e.lowStock]===null||t[e.lowStock]===void 0?r:Number(t[e.lowStock]);return{text:`${o} left`,tone:o<=c?"low":"ok"}}function T(t,n){let e=[];for(let r of t){let o=n(r);if(!e.includes(o))e.push(o)}return e}export{g as CENTS_FIELDS,u as DEFAULT_FIELDS,L as addToBasket,B as basketCount,l as basketLines,I as basketTaxable,F as basketTotal,f as canAdd,T as groupsOf,y as setQuantity,a as shelfLeft,R as stockLabel};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stored rate as a decimal multiplier.
|
|
3
|
+
*
|
|
4
|
+
* Rates are entered as percentages, because that is how tax is written down
|
|
5
|
+
* and how the dashboard asks for it. Multiplying by a percentage would charge
|
|
6
|
+
* a hundred times too much, which is the kind of error that reaches a
|
|
7
|
+
* customer's card before anyone notices.
|
|
8
|
+
*/
|
|
9
|
+
export declare function multiplierOf(rate: unknown): number;
|
|
10
|
+
export declare function breakdownFor(taxable: number, rates: readonly TaxRateRow[], options?: BreakdownOptions): TaxBreakdown;
|
|
11
|
+
/**
|
|
12
|
+
* What tax is owed on an amount, component by component.
|
|
13
|
+
*
|
|
14
|
+
* The tax rates a dashboard manages did not price anything. `recomputeOrderTotals`
|
|
15
|
+
* takes a single `taxRateId`, multiplies once, and returns one number — so a
|
|
16
|
+
* business whose tax is a sum of parts had to blend them into one rate before
|
|
17
|
+
* storing it, and a blended rate cannot answer the question that matters when
|
|
18
|
+
* a customer is exempt: exempt from *which part*?
|
|
19
|
+
*
|
|
20
|
+
* Most jurisdictions tax in parts and lift some of them for some buyers.
|
|
21
|
+
* Groceries escape VAT but not a deposit levy. A Californian medical cannabis
|
|
22
|
+
* patient is exempt from sales tax and still pays excise and the city's
|
|
23
|
+
* business tax. Blending those into one number leaves an application choosing
|
|
24
|
+
* between over-charging the exempt customer and under-collecting tax it owes.
|
|
25
|
+
*
|
|
26
|
+
* So: rates are rows, an exemption lifts the ones marked `exemptible`, and the
|
|
27
|
+
* result itemises what was charged and what was not.
|
|
28
|
+
*
|
|
29
|
+
* The calculation is pure and imports nothing, so a register in the browser
|
|
30
|
+
* shows the tax before it charges (`@stacksjs/commerce/sales-tax`); the rates
|
|
31
|
+
* a dashboard manages are read by `taxFor` in the tax module.
|
|
32
|
+
*/
|
|
33
|
+
/** One component, as it applied to this sale. */
|
|
34
|
+
export declare interface TaxComponent {
|
|
35
|
+
id: number
|
|
36
|
+
code: string
|
|
37
|
+
name: string
|
|
38
|
+
rate: number
|
|
39
|
+
amount: number
|
|
40
|
+
exempted: boolean
|
|
41
|
+
}
|
|
42
|
+
export declare interface TaxBreakdown {
|
|
43
|
+
taxable: number
|
|
44
|
+
components: TaxComponent[]
|
|
45
|
+
tax: number
|
|
46
|
+
exempted: number
|
|
47
|
+
}
|
|
48
|
+
export declare interface BreakdownOptions {
|
|
49
|
+
exempt?: boolean
|
|
50
|
+
codes?: string[]
|
|
51
|
+
country?: string
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Tax on `taxable` cents, itemised.
|
|
55
|
+
*
|
|
56
|
+
* Each component is rounded on its own rather than the total being rounded
|
|
57
|
+
* once. That is what a receipt has to show — the parts have to add up to the
|
|
58
|
+
* figure charged, and a single rounding at the end leaves a line that is a
|
|
59
|
+
* cent out from the sum above it.
|
|
60
|
+
*
|
|
61
|
+
* Every component is applied to `taxable` directly. Jurisdictions that compound
|
|
62
|
+
* — where one tax forms part of the base for another — need an explicit order,
|
|
63
|
+
* and that is a bigger claim than this should make quietly; an app that needs
|
|
64
|
+
* it should compose two calls.
|
|
65
|
+
*/
|
|
66
|
+
/** One tax rate row, in the terms the breakdown reads it. */
|
|
67
|
+
export declare interface TaxRateRow {
|
|
68
|
+
id?: number | string
|
|
69
|
+
name?: string
|
|
70
|
+
code?: string
|
|
71
|
+
rate?: number | string
|
|
72
|
+
exemptible?: boolean
|
|
73
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
function s(t){let n=Number(t);return Number.isFinite(n)?n/100:0}function b(t,n,p={}){let a=Math.max(0,Math.round(t)),m=[],i=0,u=0;for(let e of n){let x=s(e.rate),o=Math.round(a*x),r=Boolean(p.exempt&&e.exemptible);if(r)u+=o;else i+=o;m.push({id:Number(e.id),code:String(e.code??""),name:String(e.name??""),rate:x,amount:r?0:o,exempted:r})}return{taxable:a,components:m,tax:i,exempted:u}}export{b as breakdownFor,s as multiplierOf};
|
package/dist/tax/breakdown.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { breakdownFor } from '../sales-tax';
|
|
2
|
+
import type { BreakdownOptions, TaxBreakdown } from '../sales-tax';
|
|
3
|
+
export type { BreakdownOptions, TaxBreakdown, TaxComponent, TaxRateRow } from '../sales-tax';
|
|
1
4
|
/** Every active rate, in the order they should be listed. */
|
|
2
5
|
export declare function activeTaxRates(options?: BreakdownOptions): Promise<any[]>;
|
|
3
|
-
export declare function breakdownFor(taxable: number, rates: readonly TaxRateRow[], options?: BreakdownOptions): TaxBreakdown;
|
|
4
6
|
/**
|
|
5
7
|
* Read the active rates and apply them.
|
|
6
8
|
*
|
|
@@ -9,44 +11,4 @@ export declare function breakdownFor(taxable: number, rates: readonly TaxRateRow
|
|
|
9
11
|
* per bag rather than querying each time.
|
|
10
12
|
*/
|
|
11
13
|
export declare function taxFor(taxable: number, options?: BreakdownOptions): Promise<TaxBreakdown>;
|
|
12
|
-
|
|
13
|
-
export declare interface TaxComponent {
|
|
14
|
-
id: number
|
|
15
|
-
code: string
|
|
16
|
-
name: string
|
|
17
|
-
rate: number
|
|
18
|
-
amount: number
|
|
19
|
-
exempted: boolean
|
|
20
|
-
}
|
|
21
|
-
export declare interface TaxBreakdown {
|
|
22
|
-
taxable: number
|
|
23
|
-
components: TaxComponent[]
|
|
24
|
-
tax: number
|
|
25
|
-
exempted: number
|
|
26
|
-
}
|
|
27
|
-
export declare interface BreakdownOptions {
|
|
28
|
-
exempt?: boolean
|
|
29
|
-
codes?: string[]
|
|
30
|
-
country?: string
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Tax on `taxable` cents, itemised.
|
|
34
|
-
*
|
|
35
|
-
* Each component is rounded on its own rather than the total being rounded
|
|
36
|
-
* once. That is what a receipt has to show — the parts have to add up to the
|
|
37
|
-
* figure charged, and a single rounding at the end leaves a line that is a
|
|
38
|
-
* cent out from the sum above it.
|
|
39
|
-
*
|
|
40
|
-
* Every component is applied to `taxable` directly. Jurisdictions that compound
|
|
41
|
-
* — where one tax forms part of the base for another — need an explicit order,
|
|
42
|
-
* and that is a bigger claim than this should make quietly; an app that needs
|
|
43
|
-
* it should compose two calls.
|
|
44
|
-
*/
|
|
45
|
-
/** One tax rate row, in the terms the breakdown reads it. */
|
|
46
|
-
export declare interface TaxRateRow {
|
|
47
|
-
id?: number | string
|
|
48
|
-
name?: string
|
|
49
|
-
code?: string
|
|
50
|
-
rate?: number | string
|
|
51
|
-
exemptible?: boolean
|
|
52
|
-
}
|
|
14
|
+
export { breakdownFor } from '../sales-tax';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/commerce",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.75.
|
|
4
|
+
"version": "0.75.92",
|
|
5
5
|
"description": "Stacks commerce utilities.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"contributors": [
|
|
@@ -57,17 +57,17 @@
|
|
|
57
57
|
"prepublishOnly": "bun run build"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@stacksjs/cache": "0.75.
|
|
61
|
-
"@stacksjs/config": "0.75.
|
|
62
|
-
"@stacksjs/database": "0.75.
|
|
63
|
-
"@stacksjs/env": "0.75.
|
|
64
|
-
"@stacksjs/error-handling": "0.75.
|
|
65
|
-
"@stacksjs/events": "0.75.
|
|
66
|
-
"@stacksjs/logging": "0.75.
|
|
67
|
-
"@stacksjs/orm": "0.75.
|
|
68
|
-
"@stacksjs/payments": "0.75.
|
|
69
|
-
"@stacksjs/realtime": "0.75.
|
|
70
|
-
"@stacksjs/strings": "0.75.
|
|
60
|
+
"@stacksjs/cache": "0.75.92",
|
|
61
|
+
"@stacksjs/config": "0.75.92",
|
|
62
|
+
"@stacksjs/database": "0.75.92",
|
|
63
|
+
"@stacksjs/env": "0.75.92",
|
|
64
|
+
"@stacksjs/error-handling": "0.75.92",
|
|
65
|
+
"@stacksjs/events": "0.75.92",
|
|
66
|
+
"@stacksjs/logging": "0.75.92",
|
|
67
|
+
"@stacksjs/orm": "0.75.92",
|
|
68
|
+
"@stacksjs/payments": "0.75.92",
|
|
69
|
+
"@stacksjs/realtime": "0.75.92",
|
|
70
|
+
"@stacksjs/strings": "0.75.92",
|
|
71
71
|
"ts-spreadsheets": "^0.2.0"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|