@fayz-ai/plugin-inventory 0.8.0 → 0.8.3
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/InventoryContext.d.ts +2 -0
- package/dist/InventoryContext.d.ts.map +1 -1
- package/dist/InventoryPage.d.ts.map +1 -1
- package/dist/components/InventoryGeneralSettings.d.ts.map +1 -1
- package/dist/index.cjs +80 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +81 -26
- package/dist/index.js.map +1 -1
- package/dist/views/ProductCrudForm.d.ts.map +1 -1
- package/dist/views/ProductListView.d.ts.map +1 -1
- package/dist/views/RecipeFormView.d.ts.map +1 -1
- package/dist/views/RecipesView.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/InventoryContext.tsx +2 -0
- package/src/InventoryPage.tsx +13 -7
- package/src/components/InventoryGeneralSettings.tsx +21 -8
- package/src/index.ts +47 -0
- package/src/views/ProductCrudForm.tsx +6 -1
- package/src/views/ProductListView.tsx +1 -0
- package/src/views/RecipeFormView.tsx +6 -0
- package/src/views/RecipesView.tsx +6 -3
|
@@ -3,6 +3,8 @@ import type { InventoryPluginLabels } from './index';
|
|
|
3
3
|
import type { InventoryDataProvider } from './data/types';
|
|
4
4
|
import type { InventoryUIState } from './store';
|
|
5
5
|
export interface InventoryModules {
|
|
6
|
+
/** Product catalogue screens. Off when the host registers products elsewhere. */
|
|
7
|
+
products: boolean;
|
|
6
8
|
recipes: boolean;
|
|
7
9
|
stockLocations: boolean;
|
|
8
10
|
batchTracking: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InventoryContext.d.ts","sourceRoot":"","sources":["../src/InventoryContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AACxF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AACpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,OAAO,CAAA;IACvB,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAE9C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,gBAAgB,CAAA;IACzB,MAAM,EAAE,qBAAqB,CAAA;IAC7B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,YAAY,EAAE,iBAAiB,EAAE,CAAA;IACjC,SAAS,EAAE,cAAc,EAAE,CAAA;CAC5B;AAID,eAAO,MAAM,wBAAwB;;;;;EAAsB,CAAA;AAC3D,eAAO,MAAM,kBAAkB,+BAAgB,CAAA;AAC/C,eAAO,MAAM,oBAAoB,6BAAkB,CAAA;AACnD,eAAO,MAAM,iBAAiB,oDAAe,CAAA;AAE7C,OAAO,EAAE,cAAc,EAAE,CAAA"}
|
|
1
|
+
{"version":3,"file":"InventoryContext.d.ts","sourceRoot":"","sources":["../src/InventoryContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AACxF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AACpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,OAAO,CAAA;IACvB,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAA;AAE9C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,gBAAgB,CAAA;IACzB,MAAM,EAAE,qBAAqB,CAAA;IAC7B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,YAAY,EAAE,iBAAiB,EAAE,CAAA;IACjC,SAAS,EAAE,cAAc,EAAE,CAAA;CAC5B;AAID,eAAO,MAAM,wBAAwB;;;;;EAAsB,CAAA;AAC3D,eAAO,MAAM,kBAAkB,+BAAgB,CAAA;AAC/C,eAAO,MAAM,oBAAoB,6BAAkB,CAAA;AACnD,eAAO,MAAM,iBAAiB,oDAAe,CAAA;AAE7C,OAAO,EAAE,cAAc,EAAE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InventoryPage.d.ts","sourceRoot":"","sources":["../src/InventoryPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAA;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE/C,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAC3F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAqB,MAAM,eAAe,CAAA;AA+CzE,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;IACrE,MAAM,EAAE,uBAAuB,CAAA;IAC/B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAA;IACjC,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAA;CACjC,
|
|
1
|
+
{"version":3,"file":"InventoryPage.d.ts","sourceRoot":"","sources":["../src/InventoryPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAA;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE/C,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,oBAAoB,CAAA;AAC3F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAqB,MAAM,eAAe,CAAA;AA+CzE,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;IACrE,MAAM,EAAE,uBAAuB,CAAA;IAC/B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,KAAK,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAA;IACjC,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAA;CACjC,qBAqHA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InventoryGeneralSettings.d.ts","sourceRoot":"","sources":["../../src/components/InventoryGeneralSettings.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"InventoryGeneralSettings.d.ts","sourceRoot":"","sources":["../../src/components/InventoryGeneralSettings.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAgBzB,wBAAgB,wBAAwB,sBAsBvC"}
|
package/dist/index.cjs
CHANGED
|
@@ -173,7 +173,8 @@ function ProductListView({ onNew, onEdit }) {
|
|
|
173
173
|
onFacetChange: (_field, value) => setTypeFilter(value),
|
|
174
174
|
onNew,
|
|
175
175
|
addLabel: t2("inventory.productList.newProduct"),
|
|
176
|
-
onRowClick: (row) => onEdit?.(row.id)
|
|
176
|
+
onRowClick: (row) => onEdit?.(row.id),
|
|
177
|
+
feature: "inventory"
|
|
177
178
|
}
|
|
178
179
|
);
|
|
179
180
|
}
|
|
@@ -182,6 +183,7 @@ function ProductCrudForm({ editId, onSaved }) {
|
|
|
182
183
|
const { productTypes, currency } = useInventoryConfig();
|
|
183
184
|
const provider = useInventoryProvider();
|
|
184
185
|
const createProduct = useInventoryStore((s) => s.createProduct);
|
|
186
|
+
const guardProducts = saas.useLimitGuard("products");
|
|
185
187
|
const isEdit = !!editId;
|
|
186
188
|
const [initialData, setInitialData] = React2__default.default.useState(isEdit ? null : {});
|
|
187
189
|
const [headerName, setHeaderName] = React2__default.default.useState("");
|
|
@@ -233,7 +235,9 @@ function ProductCrudForm({ editId, onSaved }) {
|
|
|
233
235
|
if (isEdit && editId) {
|
|
234
236
|
await provider.updateProduct(editId, payload);
|
|
235
237
|
} else {
|
|
238
|
+
if (await guardProducts() === "blocked") return;
|
|
236
239
|
await createProduct(payload);
|
|
240
|
+
saas.invalidateLimit("products");
|
|
237
241
|
}
|
|
238
242
|
onSaved?.();
|
|
239
243
|
}
|
|
@@ -809,11 +813,11 @@ function RecipesView({ onNew, onView }) {
|
|
|
809
813
|
{
|
|
810
814
|
title: t2("inventory.recipes.title"),
|
|
811
815
|
subtitle: t2("inventory.recipes.productionFormulas", { count: String(recipes.length) }),
|
|
812
|
-
actions: onNew && /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: onNew, className: "inline-flex items-center gap-1.5 rounded-lg bg-primary border border-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors", children: [
|
|
816
|
+
actions: onNew && /* @__PURE__ */ jsxRuntime.jsx(saas.PermissionGate, { feature: "inventory", action: "create", children: /* @__PURE__ */ jsxRuntime.jsxs("button", { onClick: onNew, className: "inline-flex items-center gap-1.5 rounded-lg bg-primary border border-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-colors", children: [
|
|
813
817
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" }),
|
|
814
818
|
" ",
|
|
815
819
|
t2("inventory.recipes.newRecipe")
|
|
816
|
-
] })
|
|
820
|
+
] }) })
|
|
817
821
|
}
|
|
818
822
|
),
|
|
819
823
|
recipesLoading ? /* @__PURE__ */ jsxRuntime.jsx(RecipeSkeleton, {}) : recipes.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center rounded-lg border-2 border-dashed border-muted", children: [
|
|
@@ -872,6 +876,7 @@ function RecipeFormView({ onSaved }) {
|
|
|
872
876
|
const t2 = core.useTranslation();
|
|
873
877
|
const provider = useInventoryProvider();
|
|
874
878
|
const createRecipe = useInventoryStore((s) => s.createRecipe);
|
|
879
|
+
const guardRecipes = saas.useLimitGuard("recipes");
|
|
875
880
|
const [name, setName] = React2.useState("");
|
|
876
881
|
const [description, setDescription] = React2.useState("");
|
|
877
882
|
const [productId, setProductId] = React2.useState("");
|
|
@@ -895,6 +900,7 @@ function RecipeFormView({ onSaved }) {
|
|
|
895
900
|
ui.toast.error(t2("common.formIncomplete"));
|
|
896
901
|
return;
|
|
897
902
|
}
|
|
903
|
+
if (await guardRecipes() === "blocked") return;
|
|
898
904
|
setSaving(true);
|
|
899
905
|
try {
|
|
900
906
|
const recipe = await createRecipe({
|
|
@@ -912,6 +918,7 @@ function RecipeFormView({ onSaved }) {
|
|
|
912
918
|
notes: i.notes || void 0
|
|
913
919
|
}))
|
|
914
920
|
});
|
|
921
|
+
saas.invalidateLimit("recipes");
|
|
915
922
|
onSaved?.(recipe.id);
|
|
916
923
|
} finally {
|
|
917
924
|
setSaving(false);
|
|
@@ -1222,28 +1229,31 @@ function RecipeDetailView({ recipeId, onBack }) {
|
|
|
1222
1229
|
] })
|
|
1223
1230
|
] });
|
|
1224
1231
|
}
|
|
1232
|
+
var INVENTORY_DEFAULTS = {
|
|
1233
|
+
lowStockAlerts: true,
|
|
1234
|
+
requireReason: true,
|
|
1235
|
+
autoDeduct: false,
|
|
1236
|
+
requireSku: false,
|
|
1237
|
+
allowNegative: false,
|
|
1238
|
+
lowStockEmail: false,
|
|
1239
|
+
expiryWarnings: true
|
|
1240
|
+
};
|
|
1225
1241
|
function InventoryGeneralSettings() {
|
|
1226
1242
|
const t2 = core.useTranslation();
|
|
1243
|
+
const s = saas.useTenantPluginSettings("inventory", INVENTORY_DEFAULTS);
|
|
1227
1244
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
1228
1245
|
/* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("inventory.settings.stockManagement"), description: t2("inventory.settings.stockManagementDesc"), children: [
|
|
1229
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.lowStockAlerts"), description: t2("inventory.settings.lowStockAlertsDesc"), checked:
|
|
1230
|
-
|
|
1231
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.
|
|
1232
|
-
} }),
|
|
1233
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.autoDeduct"), description: t2("inventory.settings.autoDeductDesc"), checked: false, onChange: () => {
|
|
1234
|
-
} })
|
|
1246
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.lowStockAlerts"), description: t2("inventory.settings.lowStockAlertsDesc"), checked: s.get("lowStockAlerts"), onChange: (v) => s.set("lowStockAlerts", v) }),
|
|
1247
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.requireReason"), description: t2("inventory.settings.requireReasonDesc"), checked: s.get("requireReason"), onChange: (v) => s.set("requireReason", v) }),
|
|
1248
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.autoDeduct"), description: t2("inventory.settings.autoDeductDesc"), checked: s.get("autoDeduct"), onChange: (v) => s.set("autoDeduct", v) })
|
|
1235
1249
|
] }),
|
|
1236
1250
|
/* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("inventory.settings.products"), description: t2("inventory.settings.productsDesc"), children: [
|
|
1237
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.requireSku"), description: t2("inventory.settings.requireSkuDesc"), checked:
|
|
1238
|
-
|
|
1239
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.allowNegative"), description: t2("inventory.settings.allowNegativeDesc"), checked: false, onChange: () => {
|
|
1240
|
-
} })
|
|
1251
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.requireSku"), description: t2("inventory.settings.requireSkuDesc"), checked: s.get("requireSku"), onChange: (v) => s.set("requireSku", v) }),
|
|
1252
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.allowNegative"), description: t2("inventory.settings.allowNegativeDesc"), checked: s.get("allowNegative"), onChange: (v) => s.set("allowNegative", v) })
|
|
1241
1253
|
] }),
|
|
1242
1254
|
/* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("inventory.settings.notifications"), description: t2("inventory.settings.notificationsDesc"), children: [
|
|
1243
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.lowStockEmail"), description: t2("inventory.settings.lowStockEmailDesc"), checked:
|
|
1244
|
-
|
|
1245
|
-
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.expiryWarnings"), description: t2("inventory.settings.expiryWarningsDesc"), checked: true, onChange: () => {
|
|
1246
|
-
} })
|
|
1255
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.lowStockEmail"), description: t2("inventory.settings.lowStockEmailDesc"), checked: s.get("lowStockEmail"), onChange: (v) => s.set("lowStockEmail", v) }),
|
|
1256
|
+
/* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("inventory.settings.expiryWarnings"), description: t2("inventory.settings.expiryWarningsDesc"), checked: s.get("expiryWarnings"), onChange: (v) => s.set("expiryWarnings", v) })
|
|
1247
1257
|
] })
|
|
1248
1258
|
] });
|
|
1249
1259
|
}
|
|
@@ -1285,7 +1295,7 @@ function InventoryOnboarding({ onComplete }) {
|
|
|
1285
1295
|
function buildNav(config, view, navigate, t2) {
|
|
1286
1296
|
const items = [
|
|
1287
1297
|
{ id: "dashboard", label: t2("inventory.nav.dashboard"), icon: "BarChart3", active: view === "dashboard", onClick: () => navigate("dashboard") },
|
|
1288
|
-
{
|
|
1298
|
+
...config.modules.products ? [{
|
|
1289
1299
|
id: "products",
|
|
1290
1300
|
label: t2("inventory.nav.products"),
|
|
1291
1301
|
icon: "Package",
|
|
@@ -1294,7 +1304,7 @@ function buildNav(config, view, navigate, t2) {
|
|
|
1294
1304
|
{ id: "products-new", label: t2("inventory.nav.new"), active: view === "products-new", onClick: () => navigate("products-new") },
|
|
1295
1305
|
{ id: "products-list", label: t2("inventory.nav.list"), active: view === "products-list", onClick: () => navigate("products-list") }
|
|
1296
1306
|
]
|
|
1297
|
-
},
|
|
1307
|
+
}] : [],
|
|
1298
1308
|
{
|
|
1299
1309
|
id: "stock",
|
|
1300
1310
|
label: t2("inventory.nav.stock"),
|
|
@@ -1345,13 +1355,15 @@ function InventoryPage({ config, provider, store, registries }) {
|
|
|
1345
1355
|
const nav = buildNav(config, view, navigate, t2);
|
|
1346
1356
|
const quickActions = React2.useMemo(() => {
|
|
1347
1357
|
const actions = [
|
|
1348
|
-
|
|
1358
|
+
// Dropped with the catalogue module: offering "new product" while the
|
|
1359
|
+
// screen is gone would dead-end the user.
|
|
1360
|
+
...config.modules.products ? [{
|
|
1349
1361
|
id: "new-product",
|
|
1350
1362
|
label: t2("inventory.quickActions.newProduct"),
|
|
1351
1363
|
icon: "Package",
|
|
1352
1364
|
description: t2("inventory.quickActions.newProductDesc"),
|
|
1353
1365
|
action: () => navigate("products-new")
|
|
1354
|
-
},
|
|
1366
|
+
}] : [],
|
|
1355
1367
|
{
|
|
1356
1368
|
id: "stock-entry",
|
|
1357
1369
|
label: t2("inventory.quickActions.stockEntry"),
|
|
@@ -1388,9 +1400,13 @@ function InventoryPage({ config, provider, store, registries }) {
|
|
|
1388
1400
|
] }) }) });
|
|
1389
1401
|
}
|
|
1390
1402
|
const renderView = saas.createViewRouter([
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1403
|
+
// Routes go with the module, not just the nav entry: leaving them mounted
|
|
1404
|
+
// keeps a second product form reachable by URL.
|
|
1405
|
+
...config.modules.products ? [
|
|
1406
|
+
{ id: "products-list", render: () => /* @__PURE__ */ jsxRuntime.jsx(ProductListView, { onNew: () => navigate("products-new"), onEdit: (id) => navigate(`products-edit:${id}`) }) },
|
|
1407
|
+
{ id: "products-new", render: () => /* @__PURE__ */ jsxRuntime.jsx(ProductCrudForm, { onSaved: () => navigate("products-list") }) },
|
|
1408
|
+
{ id: "products-edit", render: ({ id }) => /* @__PURE__ */ jsxRuntime.jsx(ProductCrudForm, { editId: id, onSaved: () => navigate("products-list") }) }
|
|
1409
|
+
] : [],
|
|
1394
1410
|
{ id: "stock-entry", render: () => /* @__PURE__ */ jsxRuntime.jsx(StockMovementView, { defaultType: "entry", onSaved: () => navigate("stock-history") }) },
|
|
1395
1411
|
{ id: "stock-exit", render: () => /* @__PURE__ */ jsxRuntime.jsx(StockMovementView, { defaultType: "exit", onSaved: () => navigate("stock-history") }) },
|
|
1396
1412
|
{ id: "stock-history", render: () => /* @__PURE__ */ jsxRuntime.jsx(MovementHistoryView, { onViewDetail: (id) => navigate(`stock-detail:${id}`) }) },
|
|
@@ -2747,6 +2763,7 @@ var DEFAULT_PRODUCT_TYPES = [
|
|
|
2747
2763
|
function resolveConfig(options) {
|
|
2748
2764
|
return {
|
|
2749
2765
|
modules: {
|
|
2766
|
+
products: options?.modules?.products !== false,
|
|
2750
2767
|
recipes: options?.modules?.recipes !== false,
|
|
2751
2768
|
stockLocations: options?.modules?.stockLocations !== false,
|
|
2752
2769
|
batchTracking: options?.modules?.batchTracking ?? false
|
|
@@ -2780,6 +2797,40 @@ function createInventoryPlugin(options) {
|
|
|
2780
2797
|
{ id: "inventory", label: config.labels.pageTitle, group: config.labels.pageTitle },
|
|
2781
2798
|
...config.modules.recipes ? [{ id: "inventory.recipes", label: config.labels.recipes ?? "Recipes", group: config.labels.pageTitle }] : []
|
|
2782
2799
|
],
|
|
2800
|
+
queryEntities: [
|
|
2801
|
+
{
|
|
2802
|
+
key: "inventory:products",
|
|
2803
|
+
writable: true,
|
|
2804
|
+
entity: {
|
|
2805
|
+
name: "Product",
|
|
2806
|
+
namePlural: "Products",
|
|
2807
|
+
icon: "Package",
|
|
2808
|
+
limitKey: "products",
|
|
2809
|
+
permission: { feature: "inventory", action: "read" },
|
|
2810
|
+
fields: [
|
|
2811
|
+
{ key: "name", label: "Name", type: "text", required: true, searchable: true },
|
|
2812
|
+
{ key: "kind", label: "Kind (sale/ingredient/asset)", type: "text" },
|
|
2813
|
+
{ key: "price", label: "Price", type: "number" },
|
|
2814
|
+
{ key: "cost", label: "Cost", type: "number" },
|
|
2815
|
+
{ key: "sku", label: "SKU", type: "text", searchable: true },
|
|
2816
|
+
{ key: "isActive", label: "Active", type: "boolean" },
|
|
2817
|
+
{ key: "createdAt", label: "Created at", type: "text" }
|
|
2818
|
+
],
|
|
2819
|
+
data: {
|
|
2820
|
+
table: "products",
|
|
2821
|
+
tenantScoped: true,
|
|
2822
|
+
archetype: "product",
|
|
2823
|
+
archetypeKind: "sale",
|
|
2824
|
+
searchColumns: ["name", "sku"],
|
|
2825
|
+
defaults: { kind: "sale", status: "active", is_active: true }
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
],
|
|
2830
|
+
declaredLimits: [
|
|
2831
|
+
{ key: "products", label: "Products", table: "products" },
|
|
2832
|
+
...config.modules.recipes ? [{ key: "recipes", label: config.labels.recipes ?? "Recipes", table: "plg_inventory_recipes" }] : []
|
|
2833
|
+
],
|
|
2783
2834
|
navigation: [
|
|
2784
2835
|
{
|
|
2785
2836
|
section: options?.navSection ?? "main",
|
|
@@ -2794,7 +2845,11 @@ function createInventoryPlugin(options) {
|
|
|
2794
2845
|
{
|
|
2795
2846
|
path: "/inventory",
|
|
2796
2847
|
component: PageComponent,
|
|
2797
|
-
permission: { feature: "inventory", action: "read" }
|
|
2848
|
+
permission: { feature: "inventory", action: "read" },
|
|
2849
|
+
// Products live on this hand-built page, not a CRUD route, so nothing
|
|
2850
|
+
// would register them and a product the assistant just created would
|
|
2851
|
+
// resolve to no page at all.
|
|
2852
|
+
entityRoutes: ["product:sale", "product"]
|
|
2798
2853
|
}
|
|
2799
2854
|
],
|
|
2800
2855
|
widgets: [],
|