@fayz-ai/plugin-inventory 0.1.7 → 0.8.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/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
  [![npm](https://img.shields.io/npm/v/@fayz-ai/plugin-inventory.svg)](https://www.npmjs.com/package/@fayz-ai/plugin-inventory)
6
6
  [![license](https://img.shields.io/npm/l/@fayz-ai/plugin-inventory.svg)](https://github.com/FayaLabs/fayz-sdk/blob/main/LICENSE)
7
7
 
8
+ **Status:** beta — pre-1.0. Core surface is stable; some backend facets (see `PLUGIN_PATTERNS.md`) are still landing. APIs may change before 1.0.
9
+
8
10
  Inventory is the universal organ — a salon counts product, a restaurant counts ingredients, a clinic counts supplies. `plugin-inventory` is that organ as a plugin: a product catalog, stock entry/exit/history, optional recipes (bill of materials), and a dashboard that surfaces stock value and what's running low. It's `scope: universal`, so it snaps into any vertical.
9
11
 
10
12
  It ships real data out of the box: a Supabase provider with an automatic mock fallback, so a fresh app demos immediately and a configured one persists for real. Recipes, stock locations, and batch tracking are opt-in modules; product types, currency, and labels are configurable. The host dashboard gets inventory KPI widgets, and the AI assistant can answer "what's running low?" The intent: stop rebuilding stock control per app — compose it from one plugin into a real SaaS.
@@ -1 +1 @@
1
- {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AA4DpD,wBAAgB,+BAA+B,IAAI,qBAAqB,CA2SvE"}
1
+ {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AA6DpD,wBAAgB,+BAA+B,IAAI,qBAAqB,CA2SvE"}
@@ -0,0 +1,10 @@
1
+ export declare const T: {
2
+ readonly stockLocations: "plg_inventory_stock_locations";
3
+ readonly stockMovements: "plg_inventory_stock_movements";
4
+ readonly stockPositions: "plg_inventory_stock_positions";
5
+ readonly recipes: "plg_inventory_recipes";
6
+ readonly recipeIngredients: "plg_inventory_recipe_ingredients";
7
+ readonly measurementUnits: "plg_inventory_measurement_units";
8
+ readonly productCategories: "plg_inventory_product_categories";
9
+ };
10
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/data/tables.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,CAAC;;;;;;;;CAQJ,CAAA"}
package/dist/index.cjs CHANGED
@@ -1731,6 +1731,17 @@ function createMockInventoryProvider() {
1731
1731
  return provider;
1732
1732
  }
1733
1733
 
1734
+ // src/data/tables.ts
1735
+ var T = {
1736
+ stockLocations: "plg_inventory_stock_locations",
1737
+ stockMovements: "plg_inventory_stock_movements",
1738
+ stockPositions: "plg_inventory_stock_positions",
1739
+ recipes: "plg_inventory_recipes",
1740
+ recipeIngredients: "plg_inventory_recipe_ingredients",
1741
+ measurementUnits: "plg_inventory_measurement_units",
1742
+ productCategories: "plg_inventory_product_categories"
1743
+ };
1744
+
1734
1745
  // src/data/supabase.ts
1735
1746
  function getTenantId() {
1736
1747
  return core.getActiveTenantId();
@@ -1779,10 +1790,10 @@ function createSupabaseInventoryProvider() {
1779
1790
  function getClients() {
1780
1791
  const supabase = core.getSupabaseClientOptional();
1781
1792
  if (!supabase) throw new Error("Supabase not initialized");
1782
- return { core: supabase.schema("saas_core"), pub: supabase };
1793
+ return { core: supabase, pub: supabase };
1783
1794
  }
1784
1795
  const provider = {
1785
- // --- Products (saas_core.products) ---
1796
+ // --- Products (public.products) ---
1786
1797
  async getProducts(query) {
1787
1798
  const { core} = getClients();
1788
1799
  let qb = core.from("products").select("*", { count: "exact" });
@@ -1873,7 +1884,7 @@ function createSupabaseInventoryProvider() {
1873
1884
  if (m.destinationLocationId && !m.destinationLocationName) locationIds.add(m.destinationLocationId);
1874
1885
  }
1875
1886
  if (locationIds.size > 0) {
1876
- const { data: locs } = await pub.from("stock_locations").select("id, name").in("id", [...locationIds]);
1887
+ const { data: locs } = await pub.from(T.stockLocations).select("id, name").in("id", [...locationIds]);
1877
1888
  const locMap = new Map((locs ?? []).map((l) => [l.id, l.name]));
1878
1889
  for (const m of movements) {
1879
1890
  if (m.stockLocationId && !m.stockLocationName) m.stockLocationName = locMap.get(m.stockLocationId);
@@ -1894,7 +1905,7 @@ function createSupabaseInventoryProvider() {
1894
1905
  total_cost: unitCost * input.quantity,
1895
1906
  movement_date: input.movementDate ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
1896
1907
  };
1897
- const { data, error } = await pub.from("stock_movements").insert(row).select().single();
1908
+ const { data, error } = await pub.from(T.stockMovements).insert(row).select().single();
1898
1909
  if (error) throw new Error(error.message);
1899
1910
  const { data: product } = await core.from("products").select("id, name, stock").eq("id", input.productId).single();
1900
1911
  if (product) {
@@ -1906,11 +1917,11 @@ function createSupabaseInventoryProvider() {
1906
1917
  if (input.stockLocationId) {
1907
1918
  try {
1908
1919
  const posDelta = input.movementType === "entry" ? input.quantity : -input.quantity;
1909
- const { data: existing } = await pub.from("stock_positions").select("id, quantity").eq("product_id", input.productId).eq("stock_location_id", input.stockLocationId).maybeSingle();
1920
+ const { data: existing } = await pub.from(T.stockPositions).select("id, quantity").eq("product_id", input.productId).eq("stock_location_id", input.stockLocationId).maybeSingle();
1910
1921
  if (existing) {
1911
- await pub.from("stock_positions").update({ quantity: (existing.quantity ?? 0) + posDelta, unit_cost: input.unitCost ?? 0 }).eq("id", existing.id);
1922
+ await pub.from(T.stockPositions).update({ quantity: (existing.quantity ?? 0) + posDelta, unit_cost: input.unitCost ?? 0 }).eq("id", existing.id);
1912
1923
  } else {
1913
- await pub.from("stock_positions").insert({
1924
+ await pub.from(T.stockPositions).insert({
1914
1925
  tenant_id: tenantId,
1915
1926
  product_id: input.productId,
1916
1927
  stock_location_id: input.stockLocationId,
@@ -1921,11 +1932,11 @@ function createSupabaseInventoryProvider() {
1921
1932
  });
1922
1933
  }
1923
1934
  if (input.movementType === "transfer" && input.destinationLocationId) {
1924
- const { data: destExisting } = await pub.from("stock_positions").select("id, quantity").eq("product_id", input.productId).eq("stock_location_id", input.destinationLocationId).maybeSingle();
1935
+ const { data: destExisting } = await pub.from(T.stockPositions).select("id, quantity").eq("product_id", input.productId).eq("stock_location_id", input.destinationLocationId).maybeSingle();
1925
1936
  if (destExisting) {
1926
- await pub.from("stock_positions").update({ quantity: (destExisting.quantity ?? 0) + input.quantity, unit_cost: input.unitCost ?? 0 }).eq("id", destExisting.id);
1937
+ await pub.from(T.stockPositions).update({ quantity: (destExisting.quantity ?? 0) + input.quantity, unit_cost: input.unitCost ?? 0 }).eq("id", destExisting.id);
1927
1938
  } else {
1928
- await pub.from("stock_positions").insert({
1939
+ await pub.from(T.stockPositions).insert({
1929
1940
  tenant_id: tenantId,
1930
1941
  product_id: input.productId,
1931
1942
  stock_location_id: input.destinationLocationId,
@@ -1943,11 +1954,11 @@ function createSupabaseInventoryProvider() {
1943
1954
  const movement = snakeToCamel(data);
1944
1955
  movement.productName = product?.name;
1945
1956
  if (input.stockLocationId) {
1946
- const { data: loc } = await pub.from("stock_locations").select("name").eq("id", input.stockLocationId).single();
1957
+ const { data: loc } = await pub.from(T.stockLocations).select("name").eq("id", input.stockLocationId).single();
1947
1958
  movement.stockLocationName = loc?.name;
1948
1959
  }
1949
1960
  if (input.destinationLocationId) {
1950
- const { data: loc } = await pub.from("stock_locations").select("name").eq("id", input.destinationLocationId).single();
1961
+ const { data: loc } = await pub.from(T.stockLocations).select("name").eq("id", input.destinationLocationId).single();
1951
1962
  movement.destinationLocationName = loc?.name;
1952
1963
  }
1953
1964
  return movement;
@@ -1955,44 +1966,44 @@ function createSupabaseInventoryProvider() {
1955
1966
  // --- Stock Positions ---
1956
1967
  async getPositions(productId) {
1957
1968
  const { pub } = getClients();
1958
- const { data } = await pub.from("stock_positions").select("*").eq("product_id", productId);
1969
+ const { data } = await pub.from(T.stockPositions).select("*").eq("product_id", productId);
1959
1970
  return (data ?? []).map((r) => snakeToCamel(r));
1960
1971
  },
1961
1972
  // --- Stock Locations ---
1962
1973
  async getLocations() {
1963
1974
  const { pub } = getClients();
1964
- const { data } = await pub.from("stock_locations").select("*").eq("is_active", true).order("name");
1975
+ const { data } = await pub.from(T.stockLocations).select("*").eq("is_active", true).order("name");
1965
1976
  return (data ?? []).map((r) => snakeToCamel(r));
1966
1977
  },
1967
1978
  async createLocation(input) {
1968
1979
  const { pub } = getClients();
1969
1980
  const tenantId = getTenantId();
1970
- const { data } = await pub.from("stock_locations").insert({ ...camelToSnake(input), tenant_id: tenantId }).select().single();
1981
+ const { data } = await pub.from(T.stockLocations).insert({ ...camelToSnake(input), tenant_id: tenantId }).select().single();
1971
1982
  return snakeToCamel(data);
1972
1983
  },
1973
1984
  // --- Recipes ---
1974
1985
  async getRecipes() {
1975
1986
  const { pub } = getClients();
1976
- const { data } = await pub.from("recipes").select("*").eq("is_active", true).order("name");
1987
+ const { data } = await pub.from(T.recipes).select("*").eq("is_active", true).order("name");
1977
1988
  return (data ?? []).map((r) => snakeToCamel(r));
1978
1989
  },
1979
1990
  async getRecipeById(id) {
1980
1991
  const { pub } = getClients();
1981
- const { data } = await pub.from("recipes").select("*").eq("id", id).single();
1992
+ const { data } = await pub.from(T.recipes).select("*").eq("id", id).single();
1982
1993
  return data ? snakeToCamel(data) : null;
1983
1994
  },
1984
1995
  async getRecipeIngredients(recipeId) {
1985
1996
  const { pub } = getClients();
1986
- const { data } = await pub.from("recipe_ingredients").select("*").eq("recipe_id", recipeId).order("display_order");
1997
+ const { data } = await pub.from(T.recipeIngredients).select("*").eq("recipe_id", recipeId).order("display_order");
1987
1998
  return (data ?? []).map((r) => snakeToCamel(r));
1988
1999
  },
1989
2000
  async createRecipe(input) {
1990
2001
  const { pub } = getClients();
1991
2002
  const tenantId = getTenantId();
1992
2003
  const { ingredients, ...recipeData } = input;
1993
- const { data: recipe } = await pub.from("recipes").insert({ ...camelToSnake(recipeData), tenant_id: tenantId }).select().single();
2004
+ const { data: recipe } = await pub.from(T.recipes).insert({ ...camelToSnake(recipeData), tenant_id: tenantId }).select().single();
1994
2005
  if (recipe && ingredients.length > 0) {
1995
- await pub.from("recipe_ingredients").insert(
2006
+ await pub.from(T.recipeIngredients).insert(
1996
2007
  ingredients.map((ing) => ({ ...camelToSnake(ing), recipe_id: recipe.id, tenant_id: tenantId }))
1997
2008
  );
1998
2009
  }
@@ -2008,7 +2019,7 @@ function createSupabaseInventoryProvider() {
2008
2019
  const totalValue = items.reduce((sum, p) => sum + (p.stock ?? 0) * (p.price ?? 0), 0);
2009
2020
  const weekAgo = /* @__PURE__ */ new Date();
2010
2021
  weekAgo.setDate(weekAgo.getDate() - 7);
2011
- const { data: movements } = await pub.from("stock_movements").select("movement_type").gte("movement_date", weekAgo.toISOString().slice(0, 10));
2022
+ const { data: movements } = await pub.from(T.stockMovements).select("movement_type").gte("movement_date", weekAgo.toISOString().slice(0, 10));
2012
2023
  const movs = movements ?? [];
2013
2024
  const movementsByType = { entry: 0, exit: 0, adjustment: 0, transfer: 0, loss: 0 };
2014
2025
  for (const m of movs) movementsByType[m.movement_type]++;
@@ -2131,8 +2142,7 @@ var supplierEntity = {
2131
2142
  { key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
2132
2143
  ],
2133
2144
  data: {
2134
- table: "persons",
2135
- schema: "saas_core",
2145
+ table: "people",
2136
2146
  tenantScoped: true,
2137
2147
  filters: { kind: "supplier" },
2138
2148
  defaults: { kind: "supplier" }
@@ -2149,7 +2159,7 @@ var measurementUnitEntity = {
2149
2159
  { key: "abbreviation", label: "Abbreviation", type: "text", required: true, showInTable: true },
2150
2160
  { key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
2151
2161
  ],
2152
- data: { table: "measurement_units", tenantScoped: true }
2162
+ data: { table: T.measurementUnits, tenantScoped: true }
2153
2163
  };
2154
2164
  var productCategoryEntity = {
2155
2165
  name: "Product Category",
@@ -2162,7 +2172,7 @@ var productCategoryEntity = {
2162
2172
  { key: "parentId", label: "Parent", type: "text", showInTable: false },
2163
2173
  { key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
2164
2174
  ],
2165
- data: { table: "product_categories", tenantScoped: true }
2175
+ data: { table: T.productCategories, tenantScoped: true }
2166
2176
  };
2167
2177
  var stockLocationEntity = {
2168
2178
  name: "Stock Location",
@@ -2175,7 +2185,7 @@ var stockLocationEntity = {
2175
2185
  { key: "description", label: "Description", type: "textarea", showInTable: true },
2176
2186
  { key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true }
2177
2187
  ],
2178
- data: { table: "stock_locations", tenantScoped: true }
2188
+ data: { table: T.stockLocations, tenantScoped: true }
2179
2189
  };
2180
2190
  var inventoryRegistries = [
2181
2191
  // --- Editable ---