Commit Graph
74 Commits
Author SHA1 Message Date
Malek TellissiandClaude Sonnet 4.6 1494273efd Add viewer access to records and restrict activity log
Deploy / deploy (push) Successful in 16s
- canView() + canViewAny() in EnforcesRoles: all SMS roles can view
- View pages for all 7 resources so viewers can open records read-only
- Custom ActivityResource restricts activity log to admin + manager only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 02:44:00 +02:00
Malek TellissiandClaude Sonnet 4.6 596d66b366 Apply EnforcesRoles trait in ProductResource and ProductSaleResource
Deploy / deploy (push) Successful in 16s
Import was present but use statement inside class body was missing,
so role checks had no effect on these two resources.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 02:37:55 +02:00
Malek TellissiandClaude Sonnet 4.6 c3a3cabc2c Redirect /dashboard/login directly to Nextcloud OAuth via web route
Deploy / deploy (push) Successful in 16s
Overriding mount() conflicts with parent void return type; intercepting
the URL in routes/web.php (registered before Filament) is the clean fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 02:28:46 +02:00
Malek TellissiandClaude Sonnet 4.6 f53ea6a823 Fix login redirect: return redirect() instead of send()+exit
Deploy / deploy (push) Failing after 12s
send()+exit breaks in Livewire's mount() context; returning the
redirect response is the correct Livewire v3 approach.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 02:27:29 +02:00
Malek TellissiandClaude Sonnet 4.6 a8e7bdeb64 Redirect login page directly to Nextcloud OAuth
Deploy / deploy (push) Successful in 16s
Skip the intermediate login page entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 02:25:27 +02:00
Malek TellissiandClaude Sonnet 4.6 587501c1ad Restore missing HtmlString import in UserPanelProvider
Deploy / deploy (push) Successful in 16s
Accidentally removed when cleaning up login render hook; still needed
for the sidebar footer build-info hook.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 02:20:53 +02:00
Malek TellissiandClaude Sonnet 4.6 9ef73ca3f0 Replace login form with single Nextcloud OAuth button
Deploy / deploy (push) Successful in 17s
Removes email/password fields entirely; custom login page shows only
the blue "Mit Blue Spices Zanzibar Cloud anmelden" button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 02:18:41 +02:00
Malek TellissiandClaude Sonnet 4.6 5ea7916a09 Fix EnforcesRolesInRelation method signatures to match RelationManager
Deploy / deploy (push) Successful in 10s
canEdit and canDelete must accept Model $record to match parent class.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 02:06:01 +02:00
Malek TellissiandClaude Sonnet 4.6 0c44bfa6d0 Implement role-based access control and revert hardcoded OAuth credentials
Deploy / deploy (push) Failing after 12s
- sms-admin: full access (create, edit, delete)
- sms-manager: create and edit only, no delete
- sms-viewer: read-only, all write buttons hidden
- Apply EnforcesRolesInRelation trait to BatchesRelationManager
- Revert Nextcloud OAuth credentials to env vars (OAUTH_CLOUDSPICESBLUE_*)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 02:03:46 +02:00
Malek TellissiandClaude Sonnet 4.6 2937004118 Roles via Nextcloud groups: sms-admin/manager/viewer + fix OAuth instance_uri
Deploy / deploy (push) Successful in 29s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 01:48:24 +02:00
Malek TellissiandClaude Sonnet 4.6 fc4579c47d Add Nextcloud OAuth 2.0 login via laravel/socialite
Deploy / deploy (push) Successful in 28s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 01:40:46 +02:00
Malek TellissiandClaude Sonnet 4.6 d87383672c Add piece_weight calibration: enter Stück + Gramm per delivery, average auto-calculated
Deploy / deploy (push) Successful in 17s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 01:26:17 +02:00
Malek TellissiandClaude Sonnet 4.6 38bcf60778 Rohstofflieferung: unit suffix, enter kg stored as grams
Deploy / deploy (push) Successful in 15s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 01:06:54 +02:00
Malek TellissiandClaude Sonnet 4.6 9501ff38d9 Redirect to list after creating Rohstofflieferung
Deploy / deploy (push) Successful in 16s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 01:04:09 +02:00
Malek TellissiandClaude Sonnet 4.6 a7094b6787 SpiceChart: remove fixed width and scroll, let bars scale naturally
Deploy / deploy (push) Successful in 15s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 01:01:01 +02:00
Malek TellissiandClaude Sonnet 4.6 22fb5ed09d Rohstoffe: inline creation in Lieferung form, hide nav entry, remove quantity column
Deploy / deploy (push) Successful in 17s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 01:00:07 +02:00
Malek TellissiandClaude Sonnet 4.6 a85afbc93a SpiceChart: dynamic width + horizontal scroll for many Rohstoffe
Deploy / deploy (push) Successful in 17s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 00:58:29 +02:00
Malek TellissiandClaude Sonnet 4.6 8f7af41513 Fix SpiceChart: encode URL array as JSON.parse() to avoid double quotes breaking x-data HTML attribute
Deploy / deploy (push) Successful in 16s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 00:52:07 +02:00
Malek TellissiandClaude Sonnet 4.6 118552d093 Fix SpiceChart: query spice IDs directly in extraJsOptions and guard formatter against null regex match
Deploy / deploy (push) Successful in 15s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 00:33:05 +02:00
Malek TellissiandClaude Sonnet 4.6 7d45e3027c Fix SpiceChart: use class property for spice IDs, build edit URLs via url()
Deploy / deploy (push) Successful in 15s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-13 00:14:30 +02:00
Malek TellissiandClaude Sonnet 4.6 092a78e306 Navigate to spice edit page on chart bar click
Deploy / deploy (push) Successful in 15s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 22:38:39 +02:00
Malek TellissiandClaude Sonnet 4.6 fb19e9a579 Introduce spice_stock_movements ledger for traceable stock changes
Deploy / deploy (push) Successful in 16s
Every delivery and batch now writes a signed movement entry instead of
directly incrementing/decrementing spice.quantity. The balance is always
the sum of all movements. Existing data is backfilled in the migration.
Rohstoff edit page shows the full movement history (Lagerbewegungen tab).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 22:34:10 +02:00
Malek TellissiandClaude Sonnet 4.6 7545aed494 Fix delivery deletion: allow negative stock instead of clamping to 0
Deploy / deploy (push) Successful in 16s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 21:50:58 +02:00
Malek TellissiandClaude Sonnet 4.6 88224f887c Show git commit hash as build number in sidebar footer
Deploy / deploy (push) Successful in 16s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 21:49:42 +02:00
Malek TellissiandClaude Sonnet 4.6 70eb5170d1 Allow deleting spice deliveries even with insufficient stock; warn instead of block
Deploy / deploy (push) Successful in 15s
Shows a warning in the confirmation dialog when stock would go below zero.
Deletion proceeds and sets stock to 0 rather than blocking the action.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 21:46:47 +02:00
Malek TellissiandClaude Sonnet 4.6 a4f3254ebd Add address, phone, email, website fields to suppliers
Deploy / deploy (push) Successful in 16s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 21:38:53 +02:00
Malek TellissiandClaude Sonnet 4.6 c5b0a43928 Show recipe version and date in product list instead of recipe name
Deploy / deploy (push) Successful in 16s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 21:30:15 +02:00
Malek TellissiandClaude Sonnet 4.6 e9ea8770e5 Make Produkte a flat nav item, remove Produkte navigation group
Deploy / deploy (push) Successful in 15s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 21:28:16 +02:00
Malek TellissiandClaude Sonnet 4.6 7dfd01c11a Remove standalone RecipeResource — recipes managed via product page
Deploy / deploy (push) Successful in 9s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 21:17:38 +02:00
Malek TellissiandClaude Sonnet 4.6 352f2a0aac Remove standalone BatchResource — chargen managed via product relation manager
Deploy / deploy (push) Successful in 14s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 21:16:52 +02:00
Malek TellissiandClaude Sonnet 4.6 b90c00cf34 Fix lot_suffix input: switch to live(onBlur) to prevent typing interference
Deploy / deploy (push) Successful in 14s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 20:40:00 +02:00
Malek TellissiandClaude Sonnet 4.6 5c034ec895 Add lot_prefix to products; compose LOT number from prefix + date suffix
Deploy / deploy (push) Successful in 14s
Products now have a configurable lot_prefix (e.g. "112"). In the batch form
the prefix is shown read-only next to the suffix input (JJMMTT), and the full
lot_number (e.g. "112-260912") is composed automatically and stored.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 20:35:37 +02:00
Malek TellissiandClaude Sonnet 4.6 e9734c2590 Preserve existing origin entries when switching recipe version
Deploy / deploy (push) Successful in 14s
When the recipe is changed in a batch form, keep origin data for spices
that exist in both the old and new recipe. Only add empty rows for
spices that are new to the selected recipe version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 20:20:07 +02:00
Malek TellissiandClaude Sonnet 4.6 7c0fde4fd5 Update LOT API to use nested batch_origin_entries structure
Deploy / deploy (push) Successful in 14s
Each spice now returns an `origins` array (was single `origin` object) with
supplier and farmer resolved from the new entries relation, not flat columns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 20:07:53 +02:00
Malek TellissiandClaude Sonnet 4.6 4a98beeb45 Restructure batch origins: nested entries per ingredient with Supplier/Farmer selects
Deploy / deploy (push) Successful in 14s
Each BatchOrigin (spice) now has multiple BatchOriginEntry rows, replacing the flat
single-supplier/farmer fields. Lieferant and Landwirt are Select fields backed by
dedicated tables with inline create support. Migration preserves existing data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 20:03:05 +02:00
Malek TellissiandClaude Sonnet 4.6 400237aa5b Allow multiple supplier/farmer entries per ingredient in batch origins
Deploy / deploy (push) Successful in 14s
Each batch origin row now shows a searchable Rohstoff Select so the same
ingredient can appear in multiple rows with different suppliers or farmers.
Pre-fill from recipe still works (one row per ingredient); user clicks
'Eintrag hinzufügen' to add a second entry for the same ingredient.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 19:46:17 +02:00
Malek TellissiandClaude Sonnet 4.6 2dffe6f695 Pre-fill batch origins from recipe in standalone BatchResource
Deploy / deploy (push) Successful in 14s
Selecting a Produkt auto-sets the current Rezeptur and pre-fills one
Herkunft row per Rohstoff. Changing the Rezeptur resets the rows.
Rohstoff is shown read-only (Hidden + Placeholder); user fills in
supplier, origin country, farmer and notes only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 19:00:45 +02:00
Malek TellissiandClaude Sonnet 4.6 62be3761a9 Pre-fill batch origin rows from recipe spices automatically
Deploy / deploy (push) Successful in 14s
When creating a new batch, the Herkunft-Repeater is automatically populated
with one row per Rohstoff from the product's current recipe. The Rohstoff
field is shown as a read-only label (Hidden spice_id + Placeholder name)
so the user only fills in supplier, origin country, farmer and notes.

Changing the recipe field updates the origin rows accordingly.
On edit, existing origin data is preserved unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 18:54:00 +02:00
Malek TellissiandClaude Sonnet 4.6 6f78b22285 Rewrite RecipeHistory without InteractsWithTable to fix 404
Deploy / deploy (push) Successful in 17s
The Filament table boot lifecycle (bootedInteractsWithTable) fires before
mount(), causing issues with typed $record properties on custom resource
pages. Replaced with a plain Page + Blade view rendering recipes directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 18:38:37 +02:00
Malek TellissiandClaude Sonnet 4.6 dd710cb1c0 Fix 404 on recipe history page
Deploy / deploy (push) Successful in 14s
bootedInteractsWithTable() calls table() before mount() sets $this->record.
Accessing $this->record eagerly causes a TypeError on the uninitialized typed
property, which Filament/Livewire converts to a 404.

Deferred all $this->record accesses into closures so they only execute at
render time (after mount() has run).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 18:30:07 +02:00
Malek TellissiandClaude Sonnet 4.6 656b105cb9 Show Shopify product ID as read-only field next to product name
Deploy / deploy (push) Successful in 14s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 18:24:44 +02:00
Malek TellissiandClaude Sonnet 4.6 3301a1e9b2 Fix 500 on recipe save and surface errors as notifications
Deploy / deploy (push) Successful in 14s
- Fix root cause: recipes_spices.amount_in_percent was decimal(4,2) which
  overflows on 100% — widened to decimal(6,2) via migration
- Wrap afterSave() in try-catch so DB/logic errors show as a persistent
  danger notification instead of a blank 500 page
- Same for CreateProduct: errors show as notification; also reads
  recipe_spices from raw Livewire $this->data (not $data param which
  excludes dehydrated(false) fields) so recipes actually save on create

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 18:19:31 +02:00
Malek TellissiandClaude Sonnet 4.6 811f5a015e Add Shopify pull-from feature and fix product creation payload
Deploy / deploy (push) Successful in 14s
- Add "Aus Shopify laden" button on product edit page that pulls description
  and variant prices/SKUs/GTINs from Shopify back into SMS
- Add ShopifyService::getProduct() for fetching a single product
- Add 'options' to createProduct() payload so Shopify properly creates variants
  (without it Shopify ignores variant option values)
- Add request/response logging to createProduct() for diagnosing transfer issues
- Remove dead code from ProductResource (mutateFormData*, getRecord, getActions)
- Clean up imports in ProductResource

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 18:07:24 +02:00
Malek TellissiandClaude Sonnet 4.6 9ef24fc45f Fix 500 on product create: remove $arguments from render-time closures
Deploy / deploy (push) Successful in 15s
In Filament v3, $arguments is only injected when an action is executed
(clicked), not when it's rendered. Using $arguments in label(), icon(),
color() and visible() closures causes ErrorException: Undefined variable
$arguments on every Livewire update, including on the create page.

Simplified extraItemAction to static label/icon/color; all dynamic
logic (publish vs sync, guard for unsaved items) stays in action().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 17:39:58 +02:00
Malek TellissiandClaude Sonnet 4.6 74b47193d9 Hide 'publish to Shopify' button if any variant already has a shopify_variant_id
Deploy / deploy (push) Successful in 15s
Products imported before shopify_product_id column was added have null
there, but their variants already carry shopify_variant_id — that's
enough to know the product exists in Shopify. Running shopify:import
once will backfill shopify_product_id for all existing products.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 17:25:53 +02:00
Malek TellissiandClaude Sonnet 4.6 3450b06e32 Reload page after publishing variant to Shopify so badge updates
Deploy / deploy (push) Successful in 14s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 17:23:18 +02:00
Malek TellissiandClaude Sonnet 4.6 6fe34883c6 Add 'Publish to Shopify' for locally-created products
Deploy / deploy (push) Successful in 14s
New header action on the product edit page creates the entire product
(title, description, all variants) in Shopify via API. The returned
shopify_product_id and per-variant shopify_variant_ids are stored
locally. After success the page reloads so status badges update.
Button is hidden once the product is already linked to Shopify.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 17:22:37 +02:00
Malek TellissiandClaude Sonnet 4.6 3ebd9845b0 Add Shopify publish/sync per product variant
Deploy / deploy (push) Successful in 14s
Each size row in the product form now shows a Shopify status badge
(green ✓ Shopify / gray Lokal). For saved variants, an action button
appears: 'Zu Shopify' for local-only variants (creates via Shopify
API and stores the returned variant ID), 'Shopify sync' for linked
variants (pushes price/SKU/GTIN to Shopify).

Also fixes:
- gtin missing from ProductSize::$fillable (was silently not saved)
- shopify_product_id added to products table and stored on import
- sizes afterStateHydrated now loads ProductSize directly instead of
  via the belongsToMany pivot (gets product_size_id, shopify_variant_id)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 17:20:26 +02:00
Malek TellissiandClaude Sonnet 4.6 e187465553 Fix syncSizes: replace belongsToMany sync with ProductSize updateOrCreate
Deploy / deploy (push) Successful in 14s
belongsToMany sync() is wrong for products_sizes which is a full model
with its own id and a unique shopify_variant_id. The new approach:
- updateOrCreate per row (safe for repeated saves)
- strips non-digits from gtin, stores null when empty (fixes 500 on
  unsignedBigInteger column with empty string in strict SQL mode)
- only deletes local-only sizes (whereNull shopify_variant_id) that
  were removed — Shopify-imported sizes are never deleted here

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 17:00:58 +02:00
Malek TellissiandClaude Sonnet 4.6 e14baca7e0 Add LOT API v1 endpoint with full batch details
Deploy / deploy (push) Successful in 15s
Route changed to /api/v1/{lot_number}. LotResource now includes
packing details, units_produced, quantity_g and recipe version.
CORS config added for public API access. README documents the
endpoint, example response and subdomain setup instructions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 16:43:48 +02:00