@tokenoftrust/storefront-runner 6.3.4 → 6.4.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/apps/storefront/src/config/compliance/rulesets.ts +1 -1
- package/apps/storefront/src/lib/catalog/admin/viewmodels/catalogProducts.ts +29 -5
- package/apps/storefront/src/lib/integrations/apps/scopes.ts +3 -1
- package/apps/storefront/src/lib/integrations/webhooks/deliveryStore.ts +3 -1
- package/apps/storefront/src/lib/integrations/webhooks/events.ts +2 -0
- package/package.json +1 -1
- package/packages/public-runtime/src/compliance/evaluate.ts +35 -5
- package/packages/public-runtime/src/compliance/profile.ts +88 -15
- package/packages/public-runtime/src/compliance/ruleset.ts +5 -1
- package/packages/public-runtime/tests/fixtures/compliance-decisions.golden.json +81 -81
|
@@ -58,7 +58,7 @@ function vapeRuleset(tenantId: string): ComplianceRuleset {
|
|
|
58
58
|
{ region: "UT", banAll: true, note: "UT restricts DTC vapor shipment" },
|
|
59
59
|
{ region: "VT", banAll: true, note: "VT prohibits DTC e-cigarette shipment to consumers" },
|
|
60
60
|
{ region: "AR", banAll: true, note: "AR restricts DTC vapor shipment" },
|
|
61
|
-
// --- Flavored-ENDS bans (tobacco/menthol
|
|
61
|
+
// --- Flavored-ENDS bans (plain tobacco/unflavoured only; menthol is a characterizing flavour) ---
|
|
62
62
|
{ region: "MA", banFlavored: true, note: "MA flavored tobacco/ENDS ban" },
|
|
63
63
|
{ region: "NY", banFlavored: true, note: "NY flavored e-cigarette ban" },
|
|
64
64
|
{ region: "NJ", banFlavored: true, note: "NJ flavored vape ban" },
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
* The admin Products tab's view of the store's real catalog. Two readiness measures stay separate:
|
|
3
3
|
* listing readiness (can this product be listed: active, photographed, every variant priced and
|
|
4
4
|
* SKU'd) and catalog completeness (how many accepted catalog facts are known). Tax readiness is
|
|
5
|
-
* neither, and is not judged here.
|
|
5
|
+
* neither, and is not judged here. On a regulated store, listing readiness also requires the
|
|
6
|
+
* compliance classification checkout enforces: a product missing it is refused in flavour-ban
|
|
7
|
+
* states until the merchant classifies it
|
|
8
|
+
* (docs/devkit-staging/books/storefront-devbook-compliance-classification.md).
|
|
6
9
|
*/
|
|
7
|
-
import
|
|
10
|
+
import { complianceClassificationGaps, deriveProductProfile } from "@tot/public-runtime";
|
|
11
|
+
import type { CatalogFact, CatalogProduct, ComplianceClassificationGap } from "@tot/public-runtime";
|
|
8
12
|
import type { AdminCatalog, CatalogOrigin } from "../services/catalogProducts";
|
|
9
13
|
|
|
10
14
|
export interface ListingReadiness {
|
|
@@ -71,8 +75,28 @@ function money(amount: number, currency: string, locale: string): string {
|
|
|
71
75
|
|
|
72
76
|
const plural = (n: number, one: string, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
const CLASSIFICATION_MISSING: Record<ComplianceClassificationGap, string> = {
|
|
79
|
+
category: "Compliance category not classified",
|
|
80
|
+
flavor: "Flavour not classified",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The compliance classifications any of the product's variants is missing, in a stable order. A
|
|
85
|
+
* listing-projection record carries no metafields, so it cannot show a classification: it reports
|
|
86
|
+
* no gap rather than a false one.
|
|
87
|
+
*/
|
|
88
|
+
export function classificationGaps(product: CatalogProduct): ComplianceClassificationGap[] {
|
|
89
|
+
if (!Array.isArray(product.metafields)) return [];
|
|
90
|
+
const variants = product.variants.length ? product.variants : [null];
|
|
91
|
+
const gaps = new Set(variants.flatMap((v) => complianceClassificationGaps(deriveProductProfile(product, v))));
|
|
92
|
+
return (["category", "flavor"] as const).filter((g) => gaps.has(g));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function listingReadiness(product: CatalogProduct, regulated = false): ListingReadiness {
|
|
75
96
|
const missing: string[] = [];
|
|
97
|
+
if (regulated) {
|
|
98
|
+
for (const gap of classificationGaps(product)) missing.push(`${CLASSIFICATION_MISSING[gap]} (refused in flavour-ban states)`);
|
|
99
|
+
}
|
|
76
100
|
if (product.status !== "active") missing.push(`Status is ${product.status}`);
|
|
77
101
|
if (!product.title.trim()) missing.push("No title");
|
|
78
102
|
if (product.images.length === 0) missing.push("No photo");
|
|
@@ -95,7 +119,7 @@ export function catalogCompleteness(product: CatalogProduct): CatalogCompletenes
|
|
|
95
119
|
};
|
|
96
120
|
}
|
|
97
121
|
|
|
98
|
-
export function toCatalogProductsViewModel(catalog: AdminCatalog, locale: string): CatalogProductsViewModel {
|
|
122
|
+
export function toCatalogProductsViewModel(catalog: AdminCatalog, locale: string, regulated = false): CatalogProductsViewModel {
|
|
99
123
|
if (!catalog.ok) return { state: "error", message: catalog.message, failures: catalog.failures ?? [] };
|
|
100
124
|
const rows: CatalogProductRow[] = [];
|
|
101
125
|
const detailsByHandle: Record<string, CatalogProductDetail> = {};
|
|
@@ -111,7 +135,7 @@ export function toCatalogProductsViewModel(catalog: AdminCatalog, locale: string
|
|
|
111
135
|
variantCount: p.variants.length,
|
|
112
136
|
priceLabel: min === max ? money(min, currency, locale) : `${money(min, currency, locale)} – ${money(max, currency, locale)}`,
|
|
113
137
|
imageUrl: [...p.images].sort((a, b) => a.position - b.position)[0]?.url ?? null,
|
|
114
|
-
listing: listingReadiness(p),
|
|
138
|
+
listing: listingReadiness(p, regulated),
|
|
115
139
|
catalog: catalogCompleteness(p),
|
|
116
140
|
};
|
|
117
141
|
rows.push(row);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Scope validation — the compliance-floor gate for Storefront Private Apps.
|
|
3
3
|
* See `docs/private-apps/feature-brief.md` §"The
|
|
4
4
|
* compliance floor" and the machine-readable contract:
|
|
5
|
-
* - `docs/private-apps/contract/scopes.json` — the
|
|
5
|
+
* - `docs/private-apps/contract/scopes.json` — the 10-scope V1 catalog.
|
|
6
6
|
* - `docs/private-apps/contract/forbidden-scopes.json` — the 8 ids that map
|
|
7
7
|
* to floor clauses (`checkout:mutate`, `tax:mutate`, …) and must NEVER be
|
|
8
8
|
* grantable.
|
|
@@ -28,6 +28,8 @@ export const V1_SCOPES = [
|
|
|
28
28
|
"reports:read",
|
|
29
29
|
"attribution:write",
|
|
30
30
|
"widgets:launch",
|
|
31
|
+
"fulfillment:process",
|
|
32
|
+
"orders:read:recipient",
|
|
31
33
|
] as const;
|
|
32
34
|
export type V1Scope = (typeof V1_SCOPES)[number];
|
|
33
35
|
|
|
@@ -50,7 +50,9 @@ export type WebhookTopic =
|
|
|
50
50
|
| "order.created"
|
|
51
51
|
| "order.fulfilled"
|
|
52
52
|
| "customer.marketing_consent.updated"
|
|
53
|
-
| "attribution.finalized"
|
|
53
|
+
| "attribution.finalized"
|
|
54
|
+
| "fulfillment.assignment.created"
|
|
55
|
+
| "fulfillment.assignment.updated";
|
|
54
56
|
|
|
55
57
|
/** Mirrors `Delivery.state` in `docs/private-apps/contract/openapi.yaml` EXACTLY. */
|
|
56
58
|
export type DeliveryState = "pending" | "retrying" | "delivered" | "dead-lettered";
|
|
@@ -29,6 +29,8 @@ export const TOPIC_REQUIRED_SCOPE: Record<WebhookTopic, string | null> = {
|
|
|
29
29
|
"order.fulfilled": "orders:webhook",
|
|
30
30
|
"customer.marketing_consent.updated": "customers:read:minimal",
|
|
31
31
|
"attribution.finalized": "attribution:write",
|
|
32
|
+
"fulfillment.assignment.created": "fulfillment:process",
|
|
33
|
+
"fulfillment.assignment.updated": "fulfillment:process",
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
/** `type`'s reverse-DNS prefix (asyncapi.yaml `CloudEventEnvelope.type`). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.4.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -12,13 +12,15 @@
|
|
|
12
12
|
* enforcement wiring resolves (see `evidence-signals.ts`).
|
|
13
13
|
*
|
|
14
14
|
* Fail-closed everywhere: a regulated cart with no resolvable ruleset, an
|
|
15
|
-
* unconfigured verification provider,
|
|
15
|
+
* unconfigured verification provider, an unsatisfied excise obligation, or a
|
|
16
|
+
* product the merchant has not classified shipping into a flavour-ban
|
|
17
|
+
* jurisdiction BLOCKS.
|
|
16
18
|
* Pure + edge-safe.
|
|
17
19
|
*/
|
|
18
20
|
import type { ComplianceRuleset } from "./ruleset.js";
|
|
19
21
|
import { stateRuleFor, effectivePerOrderLimit } from "./ruleset.js";
|
|
20
|
-
import type { ProductComplianceProfile } from "./profile.js";
|
|
21
|
-
import { isRegulatedProfile } from "./profile.js";
|
|
22
|
+
import type { ComplianceClassificationGap, ProductComplianceProfile } from "./profile.js";
|
|
23
|
+
import { complianceClassificationGaps, isRegulatedProfile } from "./profile.js";
|
|
22
24
|
import type { VerificationState } from "./verification.js";
|
|
23
25
|
import { resolveVerificationDecision } from "./verification.js";
|
|
24
26
|
|
|
@@ -72,6 +74,12 @@ export interface ComplianceBlock {
|
|
|
72
74
|
sku?: string;
|
|
73
75
|
/** `flavored_prohibited` only: the characterizing flavor labels that triggered it. */
|
|
74
76
|
flavors?: string[];
|
|
77
|
+
/**
|
|
78
|
+
* `flavored_prohibited` only: the classification the product is missing. Set
|
|
79
|
+
* when the block is the fail-closed default for an unclassified product
|
|
80
|
+
* rather than a recorded characterizing flavour.
|
|
81
|
+
*/
|
|
82
|
+
unclassified?: ComplianceClassificationGap[];
|
|
75
83
|
/** True when this block is a fail-closed safety default, not a real violation. */
|
|
76
84
|
failedClosed?: boolean;
|
|
77
85
|
}
|
|
@@ -120,6 +128,11 @@ export function evaluateCartCompliance(
|
|
|
120
128
|
const blocks: ComplianceBlock[] = [];
|
|
121
129
|
const regulatedLines = input.lines.filter((l) => isRegulatedProfile(l.profile));
|
|
122
130
|
const hasRegulatedGoods = regulatedLines.length > 0;
|
|
131
|
+
// On a regulated tenant a product with no recorded category may be a
|
|
132
|
+
// regulated one: it needs a destination and fails closed where flavours are banned.
|
|
133
|
+
const unclassifiedLines = input.regulated
|
|
134
|
+
? input.lines.filter((l) => !isRegulatedProfile(l.profile) && !l.profile.categoryClassified)
|
|
135
|
+
: [];
|
|
123
136
|
|
|
124
137
|
const requirements: CartComplianceRequirements = {
|
|
125
138
|
ageVerification: "not_required",
|
|
@@ -187,7 +200,7 @@ export function evaluateCartCompliance(
|
|
|
187
200
|
|
|
188
201
|
// 5. Destination-dependent rules. If regulated goods but no ship-to state, the
|
|
189
202
|
// cart cannot clear — the state must be known to apply jurisdiction rules.
|
|
190
|
-
if (hasRegulatedGoods) {
|
|
203
|
+
if (hasRegulatedGoods || unclassifiedLines.length > 0) {
|
|
191
204
|
if (!input.destination) {
|
|
192
205
|
requirements.destination = "required";
|
|
193
206
|
blocks.push({
|
|
@@ -201,7 +214,7 @@ export function evaluateCartCompliance(
|
|
|
201
214
|
const stateRule = stateRuleFor(ruleset, region);
|
|
202
215
|
if (stateRule?.adultSignatureRequired) requirements.adultSignature = true;
|
|
203
216
|
|
|
204
|
-
for (const line of regulatedLines) {
|
|
217
|
+
for (const line of [...regulatedLines, ...unclassifiedLines]) {
|
|
205
218
|
// Per-product ToT-synced restriction (data-driven per SKU).
|
|
206
219
|
const perProductBanned = line.profile.restrictedRegions.includes(region);
|
|
207
220
|
if (stateRule?.banAll || perProductBanned) {
|
|
@@ -215,6 +228,23 @@ export function evaluateCartCompliance(
|
|
|
215
228
|
});
|
|
216
229
|
continue;
|
|
217
230
|
}
|
|
231
|
+
const gaps = complianceClassificationGaps(line.profile);
|
|
232
|
+
if (stateRule?.banFlavored && gaps.length > 0) {
|
|
233
|
+
blocks.push({
|
|
234
|
+
code: "flavored_prohibited",
|
|
235
|
+
region,
|
|
236
|
+
sku: line.sku,
|
|
237
|
+
flavors: [],
|
|
238
|
+
unclassified: gaps,
|
|
239
|
+
failedClosed: true,
|
|
240
|
+
message: `${line.title ?? line.sku} cannot be shipped to ${region}: its ${gaps
|
|
241
|
+
.map((g) => (g === "flavor" ? "flavour" : g))
|
|
242
|
+
.join(" and ")} is not classified, so it is treated as flavored (safe default = BLOCK)${
|
|
243
|
+
stateRule.note ? ` (${stateRule.note})` : ""
|
|
244
|
+
}.`,
|
|
245
|
+
});
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
218
248
|
if (stateRule?.banFlavored && line.profile.isFlavored) {
|
|
219
249
|
const flavors = line.profile.characterizingFlavors;
|
|
220
250
|
blocks.push({
|
|
@@ -4,12 +4,21 @@
|
|
|
4
4
|
* NO native compliance attributes — see `product.ts`). The signal sources, in
|
|
5
5
|
* precedence order, are the ones the ToT catalog sync already populates:
|
|
6
6
|
*
|
|
7
|
-
* 1. `metafields[compliance/*]` —
|
|
8
|
-
* (`restricted_states` JSON, `product_category`). This is the
|
|
9
|
-
* data-driven per-product
|
|
7
|
+
* 1. `metafields[compliance/*]` — per-SKU compliance classification
|
|
8
|
+
* (`restricted_states` JSON, `product_category`, `flavor`). This is the
|
|
9
|
+
* authoritative, data-driven per-product source; it is NOT a hardcoded list.
|
|
10
|
+
* `docs/devkit-staging/books/storefront-devbook-compliance-classification.md`
|
|
11
|
+
* is how a store records it.
|
|
10
12
|
* 2. `product_type` / `tags` — merchant taxonomy (nicotine/vape/e-liquid, the
|
|
11
13
|
* `F: <flavor>` tag convention the PDP already reads).
|
|
12
|
-
* 3.
|
|
14
|
+
* 3. BigCommerce custom fields (`bigcommerce_custom/Compliance Category`,
|
|
15
|
+
* `bigcommerce_custom/Flavor`), which the BigCommerce catalog extract
|
|
16
|
+
* carries as metafields.
|
|
17
|
+
* 4. the chosen variant's `option_values` (Flavor, Nicotine strength).
|
|
18
|
+
*
|
|
19
|
+
* A product the merchant has not classified is reported as such
|
|
20
|
+
* (`categoryClassified` / `flavorClassified`) so enforcement can fail closed
|
|
21
|
+
* instead of reading "no flavour label" as "unflavoured".
|
|
13
22
|
*
|
|
14
23
|
* Pure + edge-safe (no node, no I/O). The storefront resolves a product's profile
|
|
15
24
|
* once and feeds it to `evaluateCartCompliance`.
|
|
@@ -28,8 +37,8 @@ export interface ProductComplianceProfile {
|
|
|
28
37
|
/** Carries nicotine / is an ENDS product (the age-gate + excise axis). */
|
|
29
38
|
containsNicotine: boolean;
|
|
30
39
|
/**
|
|
31
|
-
* Has a characterizing flavor
|
|
32
|
-
*
|
|
40
|
+
* Has a characterizing flavor (anything but plain tobacco or unflavoured;
|
|
41
|
+
* menthol IS characterizing) — the axis several states ban for ENDS.
|
|
33
42
|
*/
|
|
34
43
|
isFlavored: boolean;
|
|
35
44
|
/**
|
|
@@ -51,15 +60,26 @@ export interface ProductComplianceProfile {
|
|
|
51
60
|
* per-SKU no-ship list — complements the tenant ruleset's jurisdiction rules.
|
|
52
61
|
*/
|
|
53
62
|
restrictedRegions: string[];
|
|
63
|
+
/**
|
|
64
|
+
* The product's regulated category is recorded: a `compliance/product_category`
|
|
65
|
+
* metafield, or a merchant `product_type` that names a kind of product. A
|
|
66
|
+
* platform-generic type (BigCommerce `physical` / `digital`) is not one.
|
|
67
|
+
*/
|
|
68
|
+
categoryClassified: boolean;
|
|
69
|
+
/** At least one flavour label is recorded (`F:` tag, flavour metafield/custom field, or variant option). */
|
|
70
|
+
flavorClassified: boolean;
|
|
54
71
|
}
|
|
55
72
|
|
|
73
|
+
/** A classification the merchant still has to record before the product can be sold everywhere. */
|
|
74
|
+
export type ComplianceClassificationGap = "category" | "flavor";
|
|
75
|
+
|
|
56
76
|
/**
|
|
57
77
|
* Exact labels that do NOT count as "characterizing" for a flavored-ENDS ban.
|
|
58
78
|
* Tobacco labels are classified by `isTobaccoFlavorLabel`.
|
|
59
79
|
*/
|
|
60
80
|
const UNFLAVORED = new Set([
|
|
61
|
-
"menthol",
|
|
62
81
|
"unflavored",
|
|
82
|
+
"unflavoured",
|
|
63
83
|
"none",
|
|
64
84
|
"original",
|
|
65
85
|
"plain",
|
|
@@ -128,6 +148,15 @@ function isCharacterizingFlavor(label: string): boolean {
|
|
|
128
148
|
return !UNFLAVORED.has(label) && !isTobaccoFlavorLabel(label);
|
|
129
149
|
}
|
|
130
150
|
|
|
151
|
+
/** Platform-default product types that say nothing about what the product is. */
|
|
152
|
+
const GENERIC_PRODUCT_TYPES = new Set(["physical", "digital", "gift_certificate"]);
|
|
153
|
+
|
|
154
|
+
/** Flavour field names a source catalog uses (variant options, BigCommerce custom fields). */
|
|
155
|
+
const FLAVOR_FIELD_NAMES = new Set(["flavor", "flavour", "flavors", "flavours"]);
|
|
156
|
+
|
|
157
|
+
/** BigCommerce custom-field names that carry the compliance category. */
|
|
158
|
+
const CATEGORY_FIELD_NAMES = new Set(["compliance category", "product category", "product_category"]);
|
|
159
|
+
|
|
131
160
|
const NICOTINE_HINTS = [
|
|
132
161
|
"nicotine",
|
|
133
162
|
"vape",
|
|
@@ -159,6 +188,17 @@ function readMetafield(
|
|
|
159
188
|
return mf?.value?.trim() || undefined;
|
|
160
189
|
}
|
|
161
190
|
|
|
191
|
+
/** A BigCommerce custom field (`bigcommerce_custom/<name>`), matched case-insensitively by name. */
|
|
192
|
+
function readCustomField(
|
|
193
|
+
product: Pick<CatalogProduct, "metafields">,
|
|
194
|
+
names: ReadonlySet<string>,
|
|
195
|
+
): string | undefined {
|
|
196
|
+
const mf = product.metafields.find(
|
|
197
|
+
(m) => m.namespace === "bigcommerce_custom" && names.has(m.key.trim().toLowerCase()),
|
|
198
|
+
);
|
|
199
|
+
return mf?.value?.trim() || undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
162
202
|
/** Parse `compliance.restricted_states` — a JSON array OR an object keyed by code. */
|
|
163
203
|
function parseRestrictedRegions(raw: string | undefined): string[] {
|
|
164
204
|
if (!raw) return [];
|
|
@@ -178,11 +218,13 @@ function parseRestrictedRegions(raw: string | undefined): string[] {
|
|
|
178
218
|
}
|
|
179
219
|
|
|
180
220
|
/**
|
|
181
|
-
* Collect flavor labels from the `F: <flavor>` tag convention
|
|
182
|
-
*
|
|
221
|
+
* Collect flavor labels from the `F: <flavor>` tag convention, the
|
|
222
|
+
* `compliance/flavor` metafield, a BigCommerce `Flavor` custom field and the
|
|
223
|
+
* chosen variant's flavour option, dropping nicotine-format labels. A
|
|
224
|
+
* metafield/custom-field value may list several labels separated by `,` `;` `|`.
|
|
183
225
|
*/
|
|
184
226
|
function collectFlavors(
|
|
185
|
-
product: Pick<CatalogProduct, "tags">,
|
|
227
|
+
product: Pick<CatalogProduct, "tags" | "metafields">,
|
|
186
228
|
variant?: Pick<CatalogVariant, "option_values"> | null,
|
|
187
229
|
): string[] {
|
|
188
230
|
const labels = new Set<string>();
|
|
@@ -190,11 +232,20 @@ function collectFlavors(
|
|
|
190
232
|
const v = raw.trim().toLowerCase();
|
|
191
233
|
if (v && !NICOTINE_FORMAT_LABELS.has(v)) labels.add(v);
|
|
192
234
|
};
|
|
235
|
+
const addList = (raw: string | undefined) => {
|
|
236
|
+
for (const part of (raw ?? "").split(/[,;|]/)) add(part);
|
|
237
|
+
};
|
|
193
238
|
for (const tag of product.tags ?? []) {
|
|
194
239
|
if (tag.startsWith("F: ")) add(tag.slice(3));
|
|
195
240
|
}
|
|
196
|
-
const
|
|
197
|
-
|
|
241
|
+
for (const mf of product.metafields ?? []) {
|
|
242
|
+
const key = mf.key.trim().toLowerCase();
|
|
243
|
+
if (mf.namespace === "compliance" && key === "flavor") addList(mf.value);
|
|
244
|
+
if (mf.namespace === "bigcommerce_custom" && FLAVOR_FIELD_NAMES.has(key)) addList(mf.value);
|
|
245
|
+
}
|
|
246
|
+
for (const [name, value] of Object.entries(variant?.option_values ?? {})) {
|
|
247
|
+
if (FLAVOR_FIELD_NAMES.has(name.trim().toLowerCase()) && value) add(value);
|
|
248
|
+
}
|
|
198
249
|
return [...labels];
|
|
199
250
|
}
|
|
200
251
|
|
|
@@ -214,10 +265,15 @@ export function deriveProductProfile(
|
|
|
214
265
|
>,
|
|
215
266
|
variant?: Pick<CatalogVariant, "option_values"> | null,
|
|
216
267
|
): ProductComplianceProfile {
|
|
217
|
-
const category =
|
|
218
|
-
|
|
268
|
+
const category =
|
|
269
|
+
readMetafield(product, "compliance", "product_category") ??
|
|
270
|
+
readCustomField(product, CATEGORY_FIELD_NAMES);
|
|
271
|
+
const productType = product.product_type?.trim().toLowerCase() ?? "";
|
|
272
|
+
const merchantType = GENERIC_PRODUCT_TYPES.has(productType) ? "" : productType;
|
|
273
|
+
const categoryClassified = Boolean(category) || merchantType.length > 0;
|
|
219
274
|
const haystack = [
|
|
220
|
-
|
|
275
|
+
merchantType,
|
|
276
|
+
(category ?? "").toLowerCase(),
|
|
221
277
|
(product.title ?? "").toLowerCase(),
|
|
222
278
|
...(product.tags ?? []).map((t) => t.toLowerCase()),
|
|
223
279
|
].join(" ");
|
|
@@ -261,9 +317,26 @@ export function deriveProductProfile(
|
|
|
261
317
|
characterizingFlavors,
|
|
262
318
|
category: cat,
|
|
263
319
|
restrictedRegions,
|
|
320
|
+
categoryClassified,
|
|
321
|
+
flavorClassified: flavorLabels.length > 0,
|
|
264
322
|
};
|
|
265
323
|
}
|
|
266
324
|
|
|
325
|
+
/**
|
|
326
|
+
* What the merchant still has to classify for this profile. A product whose
|
|
327
|
+
* category is unknown cannot be judged at all; a nicotine product with no
|
|
328
|
+
* flavour label cannot be judged in a flavour-ban jurisdiction. Enforcement
|
|
329
|
+
* blocks both there, and Admin readiness lists them.
|
|
330
|
+
*/
|
|
331
|
+
export function complianceClassificationGaps(
|
|
332
|
+
p: ProductComplianceProfile,
|
|
333
|
+
): ComplianceClassificationGap[] {
|
|
334
|
+
const gaps: ComplianceClassificationGap[] = [];
|
|
335
|
+
if (!p.categoryClassified) gaps.push("category");
|
|
336
|
+
if (p.containsNicotine && !p.flavorClassified) gaps.push("flavor");
|
|
337
|
+
return gaps;
|
|
338
|
+
}
|
|
339
|
+
|
|
267
340
|
/** True when a profile carries ANY regulated characteristic that enforcement covers. */
|
|
268
341
|
export function isRegulatedProfile(p: ProductComplianceProfile): boolean {
|
|
269
342
|
return (
|
|
@@ -22,7 +22,11 @@ export interface StateComplianceRule {
|
|
|
22
22
|
region: string;
|
|
23
23
|
/** No regulated (ENDS/vapor) shipments to this region at all (state no-ship). */
|
|
24
24
|
banAll?: boolean;
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Flavored-ENDS ban — plain tobacco and unflavoured still allowed; menthol and
|
|
27
|
+
* every other characterizing flavour are not. A product with no recorded
|
|
28
|
+
* classification is blocked here (fail closed).
|
|
29
|
+
*/
|
|
26
30
|
banFlavored?: boolean;
|
|
27
31
|
/** Per-order regulated-unit cap for this region (overrides the ruleset default). */
|
|
28
32
|
perOrderUnitLimit?: number;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"products": {
|
|
6
6
|
"unflavored": {"containsNicotine":true,"isFlavored":false,"category":"ends","regulated":true,"restrictedRegions":[]},
|
|
7
7
|
"tobacco-only": {"containsNicotine":true,"isFlavored":false,"category":"ends","regulated":true,"restrictedRegions":[]},
|
|
8
|
-
"menthol": {"containsNicotine":true,"isFlavored":
|
|
8
|
+
"menthol": {"containsNicotine":true,"isFlavored":true,"category":"ends","regulated":true,"restrictedRegions":[]},
|
|
9
9
|
"tobacco-qualified-american": {"containsNicotine":true,"isFlavored":false,"category":"ends","regulated":true,"restrictedRegions":[]},
|
|
10
10
|
"tobacco-qualified-caramel": {"containsNicotine":true,"isFlavored":true,"category":"ends","regulated":true,"restrictedRegions":[]},
|
|
11
11
|
"flavored-mango": {"containsNicotine":true,"isFlavored":true,"category":"ends","regulated":true,"restrictedRegions":[]},
|
|
@@ -161,18 +161,18 @@
|
|
|
161
161
|
{"id":"AR|non-regulated|q9","region":"AR","product":"non-regulated","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
162
162
|
{"id":"AR|non-regulated|q10","region":"AR","product":"non-regulated","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
163
163
|
{"id":"AR|non-regulated|q11","region":"AR","product":"non-regulated","quantity":11,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
164
|
-
{"id":"MA|unflavored|q1","region":"MA","product":"unflavored","quantity":1,"allowed":
|
|
165
|
-
{"id":"MA|unflavored|q9","region":"MA","product":"unflavored","quantity":9,"allowed":
|
|
166
|
-
{"id":"MA|unflavored|q10","region":"MA","product":"unflavored","quantity":10,"allowed":
|
|
167
|
-
{"id":"MA|unflavored|q11","region":"MA","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
164
|
+
{"id":"MA|unflavored|q1","region":"MA","product":"unflavored","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
165
|
+
{"id":"MA|unflavored|q9","region":"MA","product":"unflavored","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
166
|
+
{"id":"MA|unflavored|q10","region":"MA","product":"unflavored","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
167
|
+
{"id":"MA|unflavored|q11","region":"MA","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
168
168
|
{"id":"MA|tobacco-only|q1","region":"MA","product":"tobacco-only","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
169
169
|
{"id":"MA|tobacco-only|q9","region":"MA","product":"tobacco-only","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
170
170
|
{"id":"MA|tobacco-only|q10","region":"MA","product":"tobacco-only","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
171
171
|
{"id":"MA|tobacco-only|q11","region":"MA","product":"tobacco-only","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
172
|
-
{"id":"MA|menthol|q1","region":"MA","product":"menthol","quantity":1,"allowed":
|
|
173
|
-
{"id":"MA|menthol|q9","region":"MA","product":"menthol","quantity":9,"allowed":
|
|
174
|
-
{"id":"MA|menthol|q10","region":"MA","product":"menthol","quantity":10,"allowed":
|
|
175
|
-
{"id":"MA|menthol|q11","region":"MA","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
172
|
+
{"id":"MA|menthol|q1","region":"MA","product":"menthol","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
173
|
+
{"id":"MA|menthol|q9","region":"MA","product":"menthol","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
174
|
+
{"id":"MA|menthol|q10","region":"MA","product":"menthol","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
175
|
+
{"id":"MA|menthol|q11","region":"MA","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
176
176
|
{"id":"MA|tobacco-qualified-american|q1","region":"MA","product":"tobacco-qualified-american","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
177
177
|
{"id":"MA|tobacco-qualified-american|q9","region":"MA","product":"tobacco-qualified-american","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
178
178
|
{"id":"MA|tobacco-qualified-american|q10","region":"MA","product":"tobacco-qualified-american","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
@@ -189,18 +189,18 @@
|
|
|
189
189
|
{"id":"MA|flavored-via-variant-option|q9","region":"MA","product":"flavored-via-variant-option","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
190
190
|
{"id":"MA|flavored-via-variant-option|q10","region":"MA","product":"flavored-via-variant-option","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
191
191
|
{"id":"MA|flavored-via-variant-option|q11","region":"MA","product":"flavored-via-variant-option","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
192
|
-
{"id":"MA|nicotine-format-label|q1","region":"MA","product":"nicotine-format-label","quantity":1,"allowed":
|
|
193
|
-
{"id":"MA|nicotine-format-label|q9","region":"MA","product":"nicotine-format-label","quantity":9,"allowed":
|
|
194
|
-
{"id":"MA|nicotine-format-label|q10","region":"MA","product":"nicotine-format-label","quantity":10,"allowed":
|
|
195
|
-
{"id":"MA|nicotine-format-label|q11","region":"MA","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
192
|
+
{"id":"MA|nicotine-format-label|q1","region":"MA","product":"nicotine-format-label","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
193
|
+
{"id":"MA|nicotine-format-label|q9","region":"MA","product":"nicotine-format-label","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
194
|
+
{"id":"MA|nicotine-format-label|q10","region":"MA","product":"nicotine-format-label","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
195
|
+
{"id":"MA|nicotine-format-label|q11","region":"MA","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
196
196
|
{"id":"MA|zero-mg-flavored-eliquid|q1","region":"MA","product":"zero-mg-flavored-eliquid","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
197
197
|
{"id":"MA|zero-mg-flavored-eliquid|q9","region":"MA","product":"zero-mg-flavored-eliquid","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
198
198
|
{"id":"MA|zero-mg-flavored-eliquid|q10","region":"MA","product":"zero-mg-flavored-eliquid","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
199
199
|
{"id":"MA|zero-mg-flavored-eliquid|q11","region":"MA","product":"zero-mg-flavored-eliquid","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
200
|
-
{"id":"MA|per-product-restricted-tx|q1","region":"MA","product":"per-product-restricted-tx","quantity":1,"allowed":
|
|
201
|
-
{"id":"MA|per-product-restricted-tx|q9","region":"MA","product":"per-product-restricted-tx","quantity":9,"allowed":
|
|
202
|
-
{"id":"MA|per-product-restricted-tx|q10","region":"MA","product":"per-product-restricted-tx","quantity":10,"allowed":
|
|
203
|
-
{"id":"MA|per-product-restricted-tx|q11","region":"MA","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
200
|
+
{"id":"MA|per-product-restricted-tx|q1","region":"MA","product":"per-product-restricted-tx","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
201
|
+
{"id":"MA|per-product-restricted-tx|q9","region":"MA","product":"per-product-restricted-tx","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
202
|
+
{"id":"MA|per-product-restricted-tx|q10","region":"MA","product":"per-product-restricted-tx","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
203
|
+
{"id":"MA|per-product-restricted-tx|q11","region":"MA","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
204
204
|
{"id":"MA|accessory-coil|q1","region":"MA","product":"accessory-coil","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
205
205
|
{"id":"MA|accessory-coil|q9","region":"MA","product":"accessory-coil","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
206
206
|
{"id":"MA|accessory-coil|q10","region":"MA","product":"accessory-coil","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
@@ -209,18 +209,18 @@
|
|
|
209
209
|
{"id":"MA|non-regulated|q9","region":"MA","product":"non-regulated","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
210
210
|
{"id":"MA|non-regulated|q10","region":"MA","product":"non-regulated","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
211
211
|
{"id":"MA|non-regulated|q11","region":"MA","product":"non-regulated","quantity":11,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
212
|
-
{"id":"NY|unflavored|q1","region":"NY","product":"unflavored","quantity":1,"allowed":
|
|
213
|
-
{"id":"NY|unflavored|q9","region":"NY","product":"unflavored","quantity":9,"allowed":
|
|
214
|
-
{"id":"NY|unflavored|q10","region":"NY","product":"unflavored","quantity":10,"allowed":
|
|
215
|
-
{"id":"NY|unflavored|q11","region":"NY","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
212
|
+
{"id":"NY|unflavored|q1","region":"NY","product":"unflavored","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
213
|
+
{"id":"NY|unflavored|q9","region":"NY","product":"unflavored","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
214
|
+
{"id":"NY|unflavored|q10","region":"NY","product":"unflavored","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
215
|
+
{"id":"NY|unflavored|q11","region":"NY","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
216
216
|
{"id":"NY|tobacco-only|q1","region":"NY","product":"tobacco-only","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
217
217
|
{"id":"NY|tobacco-only|q9","region":"NY","product":"tobacco-only","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
218
218
|
{"id":"NY|tobacco-only|q10","region":"NY","product":"tobacco-only","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
219
219
|
{"id":"NY|tobacco-only|q11","region":"NY","product":"tobacco-only","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
220
|
-
{"id":"NY|menthol|q1","region":"NY","product":"menthol","quantity":1,"allowed":
|
|
221
|
-
{"id":"NY|menthol|q9","region":"NY","product":"menthol","quantity":9,"allowed":
|
|
222
|
-
{"id":"NY|menthol|q10","region":"NY","product":"menthol","quantity":10,"allowed":
|
|
223
|
-
{"id":"NY|menthol|q11","region":"NY","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
220
|
+
{"id":"NY|menthol|q1","region":"NY","product":"menthol","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
221
|
+
{"id":"NY|menthol|q9","region":"NY","product":"menthol","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
222
|
+
{"id":"NY|menthol|q10","region":"NY","product":"menthol","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
223
|
+
{"id":"NY|menthol|q11","region":"NY","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
224
224
|
{"id":"NY|tobacco-qualified-american|q1","region":"NY","product":"tobacco-qualified-american","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
225
225
|
{"id":"NY|tobacco-qualified-american|q9","region":"NY","product":"tobacco-qualified-american","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
226
226
|
{"id":"NY|tobacco-qualified-american|q10","region":"NY","product":"tobacco-qualified-american","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
@@ -237,18 +237,18 @@
|
|
|
237
237
|
{"id":"NY|flavored-via-variant-option|q9","region":"NY","product":"flavored-via-variant-option","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
238
238
|
{"id":"NY|flavored-via-variant-option|q10","region":"NY","product":"flavored-via-variant-option","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
239
239
|
{"id":"NY|flavored-via-variant-option|q11","region":"NY","product":"flavored-via-variant-option","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
240
|
-
{"id":"NY|nicotine-format-label|q1","region":"NY","product":"nicotine-format-label","quantity":1,"allowed":
|
|
241
|
-
{"id":"NY|nicotine-format-label|q9","region":"NY","product":"nicotine-format-label","quantity":9,"allowed":
|
|
242
|
-
{"id":"NY|nicotine-format-label|q10","region":"NY","product":"nicotine-format-label","quantity":10,"allowed":
|
|
243
|
-
{"id":"NY|nicotine-format-label|q11","region":"NY","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
240
|
+
{"id":"NY|nicotine-format-label|q1","region":"NY","product":"nicotine-format-label","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
241
|
+
{"id":"NY|nicotine-format-label|q9","region":"NY","product":"nicotine-format-label","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
242
|
+
{"id":"NY|nicotine-format-label|q10","region":"NY","product":"nicotine-format-label","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
243
|
+
{"id":"NY|nicotine-format-label|q11","region":"NY","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
244
244
|
{"id":"NY|zero-mg-flavored-eliquid|q1","region":"NY","product":"zero-mg-flavored-eliquid","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
245
245
|
{"id":"NY|zero-mg-flavored-eliquid|q9","region":"NY","product":"zero-mg-flavored-eliquid","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
246
246
|
{"id":"NY|zero-mg-flavored-eliquid|q10","region":"NY","product":"zero-mg-flavored-eliquid","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
247
247
|
{"id":"NY|zero-mg-flavored-eliquid|q11","region":"NY","product":"zero-mg-flavored-eliquid","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
248
|
-
{"id":"NY|per-product-restricted-tx|q1","region":"NY","product":"per-product-restricted-tx","quantity":1,"allowed":
|
|
249
|
-
{"id":"NY|per-product-restricted-tx|q9","region":"NY","product":"per-product-restricted-tx","quantity":9,"allowed":
|
|
250
|
-
{"id":"NY|per-product-restricted-tx|q10","region":"NY","product":"per-product-restricted-tx","quantity":10,"allowed":
|
|
251
|
-
{"id":"NY|per-product-restricted-tx|q11","region":"NY","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
248
|
+
{"id":"NY|per-product-restricted-tx|q1","region":"NY","product":"per-product-restricted-tx","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
249
|
+
{"id":"NY|per-product-restricted-tx|q9","region":"NY","product":"per-product-restricted-tx","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
250
|
+
{"id":"NY|per-product-restricted-tx|q10","region":"NY","product":"per-product-restricted-tx","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
251
|
+
{"id":"NY|per-product-restricted-tx|q11","region":"NY","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
252
252
|
{"id":"NY|accessory-coil|q1","region":"NY","product":"accessory-coil","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
253
253
|
{"id":"NY|accessory-coil|q9","region":"NY","product":"accessory-coil","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
254
254
|
{"id":"NY|accessory-coil|q10","region":"NY","product":"accessory-coil","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
@@ -257,18 +257,18 @@
|
|
|
257
257
|
{"id":"NY|non-regulated|q9","region":"NY","product":"non-regulated","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
258
258
|
{"id":"NY|non-regulated|q10","region":"NY","product":"non-regulated","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
259
259
|
{"id":"NY|non-regulated|q11","region":"NY","product":"non-regulated","quantity":11,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
260
|
-
{"id":"NJ|unflavored|q1","region":"NJ","product":"unflavored","quantity":1,"allowed":
|
|
261
|
-
{"id":"NJ|unflavored|q9","region":"NJ","product":"unflavored","quantity":9,"allowed":
|
|
262
|
-
{"id":"NJ|unflavored|q10","region":"NJ","product":"unflavored","quantity":10,"allowed":
|
|
263
|
-
{"id":"NJ|unflavored|q11","region":"NJ","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
260
|
+
{"id":"NJ|unflavored|q1","region":"NJ","product":"unflavored","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
261
|
+
{"id":"NJ|unflavored|q9","region":"NJ","product":"unflavored","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
262
|
+
{"id":"NJ|unflavored|q10","region":"NJ","product":"unflavored","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
263
|
+
{"id":"NJ|unflavored|q11","region":"NJ","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
264
264
|
{"id":"NJ|tobacco-only|q1","region":"NJ","product":"tobacco-only","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
265
265
|
{"id":"NJ|tobacco-only|q9","region":"NJ","product":"tobacco-only","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
266
266
|
{"id":"NJ|tobacco-only|q10","region":"NJ","product":"tobacco-only","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
267
267
|
{"id":"NJ|tobacco-only|q11","region":"NJ","product":"tobacco-only","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
268
|
-
{"id":"NJ|menthol|q1","region":"NJ","product":"menthol","quantity":1,"allowed":
|
|
269
|
-
{"id":"NJ|menthol|q9","region":"NJ","product":"menthol","quantity":9,"allowed":
|
|
270
|
-
{"id":"NJ|menthol|q10","region":"NJ","product":"menthol","quantity":10,"allowed":
|
|
271
|
-
{"id":"NJ|menthol|q11","region":"NJ","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
268
|
+
{"id":"NJ|menthol|q1","region":"NJ","product":"menthol","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
269
|
+
{"id":"NJ|menthol|q9","region":"NJ","product":"menthol","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
270
|
+
{"id":"NJ|menthol|q10","region":"NJ","product":"menthol","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
271
|
+
{"id":"NJ|menthol|q11","region":"NJ","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
272
272
|
{"id":"NJ|tobacco-qualified-american|q1","region":"NJ","product":"tobacco-qualified-american","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
273
273
|
{"id":"NJ|tobacco-qualified-american|q9","region":"NJ","product":"tobacco-qualified-american","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
274
274
|
{"id":"NJ|tobacco-qualified-american|q10","region":"NJ","product":"tobacco-qualified-american","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
@@ -285,18 +285,18 @@
|
|
|
285
285
|
{"id":"NJ|flavored-via-variant-option|q9","region":"NJ","product":"flavored-via-variant-option","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
286
286
|
{"id":"NJ|flavored-via-variant-option|q10","region":"NJ","product":"flavored-via-variant-option","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
287
287
|
{"id":"NJ|flavored-via-variant-option|q11","region":"NJ","product":"flavored-via-variant-option","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
288
|
-
{"id":"NJ|nicotine-format-label|q1","region":"NJ","product":"nicotine-format-label","quantity":1,"allowed":
|
|
289
|
-
{"id":"NJ|nicotine-format-label|q9","region":"NJ","product":"nicotine-format-label","quantity":9,"allowed":
|
|
290
|
-
{"id":"NJ|nicotine-format-label|q10","region":"NJ","product":"nicotine-format-label","quantity":10,"allowed":
|
|
291
|
-
{"id":"NJ|nicotine-format-label|q11","region":"NJ","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
288
|
+
{"id":"NJ|nicotine-format-label|q1","region":"NJ","product":"nicotine-format-label","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
289
|
+
{"id":"NJ|nicotine-format-label|q9","region":"NJ","product":"nicotine-format-label","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
290
|
+
{"id":"NJ|nicotine-format-label|q10","region":"NJ","product":"nicotine-format-label","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
291
|
+
{"id":"NJ|nicotine-format-label|q11","region":"NJ","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
292
292
|
{"id":"NJ|zero-mg-flavored-eliquid|q1","region":"NJ","product":"zero-mg-flavored-eliquid","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
293
293
|
{"id":"NJ|zero-mg-flavored-eliquid|q9","region":"NJ","product":"zero-mg-flavored-eliquid","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
294
294
|
{"id":"NJ|zero-mg-flavored-eliquid|q10","region":"NJ","product":"zero-mg-flavored-eliquid","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
295
295
|
{"id":"NJ|zero-mg-flavored-eliquid|q11","region":"NJ","product":"zero-mg-flavored-eliquid","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
296
|
-
{"id":"NJ|per-product-restricted-tx|q1","region":"NJ","product":"per-product-restricted-tx","quantity":1,"allowed":
|
|
297
|
-
{"id":"NJ|per-product-restricted-tx|q9","region":"NJ","product":"per-product-restricted-tx","quantity":9,"allowed":
|
|
298
|
-
{"id":"NJ|per-product-restricted-tx|q10","region":"NJ","product":"per-product-restricted-tx","quantity":10,"allowed":
|
|
299
|
-
{"id":"NJ|per-product-restricted-tx|q11","region":"NJ","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
296
|
+
{"id":"NJ|per-product-restricted-tx|q1","region":"NJ","product":"per-product-restricted-tx","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
297
|
+
{"id":"NJ|per-product-restricted-tx|q9","region":"NJ","product":"per-product-restricted-tx","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
298
|
+
{"id":"NJ|per-product-restricted-tx|q10","region":"NJ","product":"per-product-restricted-tx","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
299
|
+
{"id":"NJ|per-product-restricted-tx|q11","region":"NJ","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
300
300
|
{"id":"NJ|accessory-coil|q1","region":"NJ","product":"accessory-coil","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
301
301
|
{"id":"NJ|accessory-coil|q9","region":"NJ","product":"accessory-coil","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
302
302
|
{"id":"NJ|accessory-coil|q10","region":"NJ","product":"accessory-coil","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
@@ -305,18 +305,18 @@
|
|
|
305
305
|
{"id":"NJ|non-regulated|q9","region":"NJ","product":"non-regulated","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
306
306
|
{"id":"NJ|non-regulated|q10","region":"NJ","product":"non-regulated","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
307
307
|
{"id":"NJ|non-regulated|q11","region":"NJ","product":"non-regulated","quantity":11,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
308
|
-
{"id":"RI|unflavored|q1","region":"RI","product":"unflavored","quantity":1,"allowed":
|
|
309
|
-
{"id":"RI|unflavored|q9","region":"RI","product":"unflavored","quantity":9,"allowed":
|
|
310
|
-
{"id":"RI|unflavored|q10","region":"RI","product":"unflavored","quantity":10,"allowed":
|
|
311
|
-
{"id":"RI|unflavored|q11","region":"RI","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
308
|
+
{"id":"RI|unflavored|q1","region":"RI","product":"unflavored","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
309
|
+
{"id":"RI|unflavored|q9","region":"RI","product":"unflavored","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
310
|
+
{"id":"RI|unflavored|q10","region":"RI","product":"unflavored","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
311
|
+
{"id":"RI|unflavored|q11","region":"RI","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
312
312
|
{"id":"RI|tobacco-only|q1","region":"RI","product":"tobacco-only","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
313
313
|
{"id":"RI|tobacco-only|q9","region":"RI","product":"tobacco-only","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
314
314
|
{"id":"RI|tobacco-only|q10","region":"RI","product":"tobacco-only","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
315
315
|
{"id":"RI|tobacco-only|q11","region":"RI","product":"tobacco-only","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
316
|
-
{"id":"RI|menthol|q1","region":"RI","product":"menthol","quantity":1,"allowed":
|
|
317
|
-
{"id":"RI|menthol|q9","region":"RI","product":"menthol","quantity":9,"allowed":
|
|
318
|
-
{"id":"RI|menthol|q10","region":"RI","product":"menthol","quantity":10,"allowed":
|
|
319
|
-
{"id":"RI|menthol|q11","region":"RI","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
316
|
+
{"id":"RI|menthol|q1","region":"RI","product":"menthol","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
317
|
+
{"id":"RI|menthol|q9","region":"RI","product":"menthol","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
318
|
+
{"id":"RI|menthol|q10","region":"RI","product":"menthol","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
319
|
+
{"id":"RI|menthol|q11","region":"RI","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
320
320
|
{"id":"RI|tobacco-qualified-american|q1","region":"RI","product":"tobacco-qualified-american","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
321
321
|
{"id":"RI|tobacco-qualified-american|q9","region":"RI","product":"tobacco-qualified-american","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
322
322
|
{"id":"RI|tobacco-qualified-american|q10","region":"RI","product":"tobacco-qualified-american","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
@@ -333,18 +333,18 @@
|
|
|
333
333
|
{"id":"RI|flavored-via-variant-option|q9","region":"RI","product":"flavored-via-variant-option","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
334
334
|
{"id":"RI|flavored-via-variant-option|q10","region":"RI","product":"flavored-via-variant-option","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
335
335
|
{"id":"RI|flavored-via-variant-option|q11","region":"RI","product":"flavored-via-variant-option","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
336
|
-
{"id":"RI|nicotine-format-label|q1","region":"RI","product":"nicotine-format-label","quantity":1,"allowed":
|
|
337
|
-
{"id":"RI|nicotine-format-label|q9","region":"RI","product":"nicotine-format-label","quantity":9,"allowed":
|
|
338
|
-
{"id":"RI|nicotine-format-label|q10","region":"RI","product":"nicotine-format-label","quantity":10,"allowed":
|
|
339
|
-
{"id":"RI|nicotine-format-label|q11","region":"RI","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
336
|
+
{"id":"RI|nicotine-format-label|q1","region":"RI","product":"nicotine-format-label","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
337
|
+
{"id":"RI|nicotine-format-label|q9","region":"RI","product":"nicotine-format-label","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
338
|
+
{"id":"RI|nicotine-format-label|q10","region":"RI","product":"nicotine-format-label","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
339
|
+
{"id":"RI|nicotine-format-label|q11","region":"RI","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
340
340
|
{"id":"RI|zero-mg-flavored-eliquid|q1","region":"RI","product":"zero-mg-flavored-eliquid","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
341
341
|
{"id":"RI|zero-mg-flavored-eliquid|q9","region":"RI","product":"zero-mg-flavored-eliquid","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
342
342
|
{"id":"RI|zero-mg-flavored-eliquid|q10","region":"RI","product":"zero-mg-flavored-eliquid","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
343
343
|
{"id":"RI|zero-mg-flavored-eliquid|q11","region":"RI","product":"zero-mg-flavored-eliquid","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
344
|
-
{"id":"RI|per-product-restricted-tx|q1","region":"RI","product":"per-product-restricted-tx","quantity":1,"allowed":
|
|
345
|
-
{"id":"RI|per-product-restricted-tx|q9","region":"RI","product":"per-product-restricted-tx","quantity":9,"allowed":
|
|
346
|
-
{"id":"RI|per-product-restricted-tx|q10","region":"RI","product":"per-product-restricted-tx","quantity":10,"allowed":
|
|
347
|
-
{"id":"RI|per-product-restricted-tx|q11","region":"RI","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
344
|
+
{"id":"RI|per-product-restricted-tx|q1","region":"RI","product":"per-product-restricted-tx","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
345
|
+
{"id":"RI|per-product-restricted-tx|q9","region":"RI","product":"per-product-restricted-tx","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
346
|
+
{"id":"RI|per-product-restricted-tx|q10","region":"RI","product":"per-product-restricted-tx","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
347
|
+
{"id":"RI|per-product-restricted-tx|q11","region":"RI","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
348
348
|
{"id":"RI|accessory-coil|q1","region":"RI","product":"accessory-coil","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
349
349
|
{"id":"RI|accessory-coil|q9","region":"RI","product":"accessory-coil","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
350
350
|
{"id":"RI|accessory-coil|q10","region":"RI","product":"accessory-coil","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
@@ -353,18 +353,18 @@
|
|
|
353
353
|
{"id":"RI|non-regulated|q9","region":"RI","product":"non-regulated","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
354
354
|
{"id":"RI|non-regulated|q10","region":"RI","product":"non-regulated","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
355
355
|
{"id":"RI|non-regulated|q11","region":"RI","product":"non-regulated","quantity":11,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":null,"limitExceeded":false},
|
|
356
|
-
{"id":"CA|unflavored|q1","region":"CA","product":"unflavored","quantity":1,"allowed":
|
|
357
|
-
{"id":"CA|unflavored|q9","region":"CA","product":"unflavored","quantity":9,"allowed":
|
|
358
|
-
{"id":"CA|unflavored|q10","region":"CA","product":"unflavored","quantity":10,"allowed":
|
|
359
|
-
{"id":"CA|unflavored|q11","region":"CA","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
356
|
+
{"id":"CA|unflavored|q1","region":"CA","product":"unflavored","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
357
|
+
{"id":"CA|unflavored|q9","region":"CA","product":"unflavored","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
358
|
+
{"id":"CA|unflavored|q10","region":"CA","product":"unflavored","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
359
|
+
{"id":"CA|unflavored|q11","region":"CA","product":"unflavored","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
360
360
|
{"id":"CA|tobacco-only|q1","region":"CA","product":"tobacco-only","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
361
361
|
{"id":"CA|tobacco-only|q9","region":"CA","product":"tobacco-only","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
362
362
|
{"id":"CA|tobacco-only|q10","region":"CA","product":"tobacco-only","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
363
363
|
{"id":"CA|tobacco-only|q11","region":"CA","product":"tobacco-only","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
364
|
-
{"id":"CA|menthol|q1","region":"CA","product":"menthol","quantity":1,"allowed":
|
|
365
|
-
{"id":"CA|menthol|q9","region":"CA","product":"menthol","quantity":9,"allowed":
|
|
366
|
-
{"id":"CA|menthol|q10","region":"CA","product":"menthol","quantity":10,"allowed":
|
|
367
|
-
{"id":"CA|menthol|q11","region":"CA","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
364
|
+
{"id":"CA|menthol|q1","region":"CA","product":"menthol","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
365
|
+
{"id":"CA|menthol|q9","region":"CA","product":"menthol","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
366
|
+
{"id":"CA|menthol|q10","region":"CA","product":"menthol","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
367
|
+
{"id":"CA|menthol|q11","region":"CA","product":"menthol","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
368
368
|
{"id":"CA|tobacco-qualified-american|q1","region":"CA","product":"tobacco-qualified-american","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
369
369
|
{"id":"CA|tobacco-qualified-american|q9","region":"CA","product":"tobacco-qualified-american","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
370
370
|
{"id":"CA|tobacco-qualified-american|q10","region":"CA","product":"tobacco-qualified-american","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
@@ -381,18 +381,18 @@
|
|
|
381
381
|
{"id":"CA|flavored-via-variant-option|q9","region":"CA","product":"flavored-via-variant-option","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
382
382
|
{"id":"CA|flavored-via-variant-option|q10","region":"CA","product":"flavored-via-variant-option","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
383
383
|
{"id":"CA|flavored-via-variant-option|q11","region":"CA","product":"flavored-via-variant-option","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
384
|
-
{"id":"CA|nicotine-format-label|q1","region":"CA","product":"nicotine-format-label","quantity":1,"allowed":
|
|
385
|
-
{"id":"CA|nicotine-format-label|q9","region":"CA","product":"nicotine-format-label","quantity":9,"allowed":
|
|
386
|
-
{"id":"CA|nicotine-format-label|q10","region":"CA","product":"nicotine-format-label","quantity":10,"allowed":
|
|
387
|
-
{"id":"CA|nicotine-format-label|q11","region":"CA","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
384
|
+
{"id":"CA|nicotine-format-label|q1","region":"CA","product":"nicotine-format-label","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
385
|
+
{"id":"CA|nicotine-format-label|q9","region":"CA","product":"nicotine-format-label","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
386
|
+
{"id":"CA|nicotine-format-label|q10","region":"CA","product":"nicotine-format-label","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
387
|
+
{"id":"CA|nicotine-format-label|q11","region":"CA","product":"nicotine-format-label","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
388
388
|
{"id":"CA|zero-mg-flavored-eliquid|q1","region":"CA","product":"zero-mg-flavored-eliquid","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
389
389
|
{"id":"CA|zero-mg-flavored-eliquid|q9","region":"CA","product":"zero-mg-flavored-eliquid","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
390
390
|
{"id":"CA|zero-mg-flavored-eliquid|q10","region":"CA","product":"zero-mg-flavored-eliquid","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
391
391
|
{"id":"CA|zero-mg-flavored-eliquid|q11","region":"CA","product":"zero-mg-flavored-eliquid","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
392
|
-
{"id":"CA|per-product-restricted-tx|q1","region":"CA","product":"per-product-restricted-tx","quantity":1,"allowed":
|
|
393
|
-
{"id":"CA|per-product-restricted-tx|q9","region":"CA","product":"per-product-restricted-tx","quantity":9,"allowed":
|
|
394
|
-
{"id":"CA|per-product-restricted-tx|q10","region":"CA","product":"per-product-restricted-tx","quantity":10,"allowed":
|
|
395
|
-
{"id":"CA|per-product-restricted-tx|q11","region":"CA","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
392
|
+
{"id":"CA|per-product-restricted-tx|q1","region":"CA","product":"per-product-restricted-tx","quantity":1,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
393
|
+
{"id":"CA|per-product-restricted-tx|q9","region":"CA","product":"per-product-restricted-tx","quantity":9,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
394
|
+
{"id":"CA|per-product-restricted-tx|q10","region":"CA","product":"per-product-restricted-tx","quantity":10,"allowed":false,"action":"blocked","reasons":["flavored_prohibited"],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
395
|
+
{"id":"CA|per-product-restricted-tx|q11","region":"CA","product":"per-product-restricted-tx","quantity":11,"allowed":false,"action":"blocked","reasons":["flavored_prohibited","purchase_limit_exceeded"],"adultSignature":true,"limit":10,"limitExceeded":true},
|
|
396
396
|
{"id":"CA|accessory-coil|q1","region":"CA","product":"accessory-coil","quantity":1,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
397
397
|
{"id":"CA|accessory-coil|q9","region":"CA","product":"accessory-coil","quantity":9,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|
|
398
398
|
{"id":"CA|accessory-coil|q10","region":"CA","product":"accessory-coil","quantity":10,"allowed":true,"action":"clear","reasons":[],"adultSignature":true,"limit":10,"limitExceeded":false},
|