diff --git a/app/Models/WooOrder.php b/app/Models/WooOrder.php index eb66459..2812fd9 100644 --- a/app/Models/WooOrder.php +++ b/app/Models/WooOrder.php @@ -65,8 +65,13 @@ class WooOrder extends Model public function totalFormatted(): string { $minor = (int) $this->total_minor; - $currency = strtoupper((string) ($this->currency ?: 'GHS')); + $currency = strtoupper(trim((string) ($this->currency ?: ''))); + if ($currency === '' && ($this->relationLoaded('store') || $this->store)) { + $currency = $this->store->currency(); + } - return $currency.' '.number_format($minor / 100, 2); + $amount = number_format($minor / 100, 2); + + return $currency !== '' ? $currency.' '.$amount : $amount; } } diff --git a/app/Models/WooProduct.php b/app/Models/WooProduct.php index 85a0e74..877e5c2 100644 --- a/app/Models/WooProduct.php +++ b/app/Models/WooProduct.php @@ -64,9 +64,24 @@ class WooProduct extends Model return '—'; } - $currency = strtoupper((string) ($this->store?->metadata['currency'] ?? 'GHS')); + if ($this->relationLoaded('store') || $this->store) { + return $this->store->formatMoney($minor); + } - return $currency.' '.number_format($minor / 100, 2); + return number_format($minor / 100, 2); + } + + public function regularPriceFormatted(): string + { + if ($this->regular_price_minor === null) { + return '—'; + } + + if ($this->relationLoaded('store') || $this->store) { + return $this->store->formatMoney((int) $this->regular_price_minor); + } + + return number_format(((int) $this->regular_price_minor) / 100, 2); } public function statusLabel(): string diff --git a/app/Models/WooStore.php b/app/Models/WooStore.php index 8cfdfb1..662cd0c 100644 --- a/app/Models/WooStore.php +++ b/app/Models/WooStore.php @@ -70,4 +70,39 @@ class WooStore extends Model { return rtrim((string) config('app.url'), '/').'/webhooks/woocommerce/'.$this->public_id; } + + /** + * WooCommerce store currency (ISO 4217), synced from the connected site. + * Does not fall back to GHS — unknown currency is empty until the next sync. + */ + public function currency(): string + { + $meta = is_array($this->metadata) ? $this->metadata : []; + $currency = strtoupper(trim((string) ($meta['currency'] ?? ''))); + + return preg_match('/^[A-Z]{3}$/', $currency) === 1 ? $currency : ''; + } + + public function currencySymbol(): string + { + $meta = is_array($this->metadata) ? $this->metadata : []; + $symbol = trim((string) ($meta['currency_symbol'] ?? '')); + if ($symbol !== '') { + return $symbol; + } + + return $this->currency(); + } + + public function formatMoney(int|float|null $minor): string + { + if ($minor === null) { + return '—'; + } + + $currency = $this->currency(); + $amount = number_format(((int) $minor) / 100, 2); + + return $currency !== '' ? $currency.' '.$amount : $amount; + } } diff --git a/app/Services/Woo/CatalogSyncService.php b/app/Services/Woo/CatalogSyncService.php index 38449fd..3ef5eb4 100644 --- a/app/Services/Woo/CatalogSyncService.php +++ b/app/Services/Woo/CatalogSyncService.php @@ -16,12 +16,53 @@ class CatalogSyncService public function syncAll(WooStore $store, ?User $user = null): array { + $this->syncStoreMeta($store); + return [ 'categories' => $this->syncCategories($store), 'products' => $this->syncProducts($store, $user), ]; } + /** + * Pull store-level settings (currency, site name) from the WooCommerce plugin. + */ + public function syncStoreMeta(WooStore $store): void + { + $response = $this->client->get($store, 'store'); + if (! is_array($response)) { + return; + } + + $this->applyStoreMeta($store, $response); + } + + /** @param array $payload */ + public function applyStoreMeta(WooStore $store, array $payload): void + { + $currency = strtoupper(trim((string) ($payload['currency'] ?? ''))); + if (preg_match('/^[A-Z]{3}$/', $currency) !== 1) { + return; + } + + $meta = is_array($store->metadata) ? $store->metadata : []; + $meta['currency'] = $currency; + + $symbol = trim((string) ($payload['currency_symbol'] ?? '')); + if ($symbol !== '') { + $meta['currency_symbol'] = $symbol; + } + + $siteName = trim((string) ($payload['site_name'] ?? '')); + $updates = ['metadata' => $meta]; + if ($siteName !== '' && (string) $store->site_name !== $siteName) { + $updates['site_name'] = $siteName; + } + + $store->forceFill($updates)->save(); + $store->refresh(); + } + public function syncCategories(WooStore $store): int { $response = $this->client->get($store, 'categories', ['per_page' => 100]); @@ -53,11 +94,20 @@ class CatalogSyncService return 0; } + if (isset($response['currency']) || isset($response['currency_symbol'])) { + $this->applyStoreMeta($store, $response); + } + $items = $response['data'] ?? $response; if (! is_array($items)) { return 0; } + // Bare list responses are numeric arrays of products — ignore non-product keys. + if (array_is_list($items) === false && isset($response['data']) === false) { + $items = array_values(array_filter($items, fn ($item) => is_array($item) && isset($item['id']))); + } + $count = 0; foreach ($items as $item) { if (! is_array($item)) { diff --git a/app/Services/Woo/OrderIngestService.php b/app/Services/Woo/OrderIngestService.php index e726987..63cdfaf 100644 --- a/app/Services/Woo/OrderIngestService.php +++ b/app/Services/Woo/OrderIngestService.php @@ -30,7 +30,13 @@ class OrderIngestService ->all(); $totalMinor = (int) round(((float) ($payload['total'] ?? 0)) * 100); - $currency = strtoupper((string) ($payload['currency'] ?? 'GHS')); + $currency = strtoupper(trim((string) ($payload['currency'] ?? ''))); + if (preg_match('/^[A-Z]{3}$/', $currency) !== 1) { + $currency = $store->currency(); + } + if ($currency === '') { + $currency = 'XXX'; + } $wcUpdatedAt = isset($payload['date_modified_gmt']) ? Carbon::parse($payload['date_modified_gmt'].' UTC') diff --git a/app/Services/Woo/OrderSyncService.php b/app/Services/Woo/OrderSyncService.php index aa6db3b..f35cddf 100644 --- a/app/Services/Woo/OrderSyncService.php +++ b/app/Services/Woo/OrderSyncService.php @@ -9,6 +9,7 @@ class OrderSyncService public function __construct( private PluginClient $client, private OrderIngestService $orders, + private CatalogSyncService $catalog, ) {} public function syncOrders(WooStore $store): int @@ -18,11 +19,19 @@ class OrderSyncService return 0; } + if (isset($response['currency']) || isset($response['currency_symbol'])) { + $this->catalog->applyStoreMeta($store, $response); + } + $items = $response['data'] ?? $response; if (! is_array($items)) { return 0; } + if (array_is_list($items) === false && isset($response['data']) === false) { + $items = array_values(array_filter($items, fn ($item) => is_array($item) && (isset($item['id']) || isset($item['number'])))); + } + $count = 0; foreach ($items as $item) { if (! is_array($item)) { diff --git a/public/downloads/ladill-woo-plugin.zip b/public/downloads/ladill-woo-plugin.zip index 375d695..3c46b15 100644 Binary files a/public/downloads/ladill-woo-plugin.zip and b/public/downloads/ladill-woo-plugin.zip differ diff --git a/resources/views/woo/products/form.blade.php b/resources/views/woo/products/form.blade.php index 5f87177..a6041a0 100644 --- a/resources/views/woo/products/form.blade.php +++ b/resources/views/woo/products/form.blade.php @@ -40,16 +40,37 @@ + @php + $storeCurrency = $currentStore->currency(); + $storeCurrencyLabel = $storeCurrency !== '' ? $storeCurrency : 'store currency'; + @endphp
- - + +
+ @if($storeCurrency !== '') + {{ $storeCurrency }} + @endif + +
- - + +
+ @if($storeCurrency !== '') + {{ $storeCurrency }} + @endif + +
+ @if($storeCurrency === '') +

Store currency not synced yet. Run Sync from WooCommerce on the products page so prices use your WooCommerce currency.

+ @endif