pricing update
This commit is contained in:
@@ -49,7 +49,7 @@ import {
|
||||
} from '../services/fiestaQueries';
|
||||
import { FIESTA_TENANT_ID, FIESTA_PRIMARY_LOCATION_ID, str as fstr } from '../services/fiestaApi';
|
||||
import { stockRowToProduct, stockRowToInventory } from '../services/fiestaMappers';
|
||||
import { useStoreCatalogue, isPublishedItem } from '../services/storeCatalogue';
|
||||
import { useStoreCatalogue, usePriceEverywhere, isPublishedItem } from '../services/storeCatalogue';
|
||||
import BulkCartDrawer from './BulkCartDrawer';
|
||||
import AwaitingApi from './AwaitingApi';
|
||||
import { SlideDrawer, Skeleton, TH_STYLE, SURFACE_ALT, TEXT, TEXT_2, TEXT_3, BORDER, BRAND, tint, edge, StatusChip } from './consoleUi';
|
||||
@@ -99,10 +99,19 @@ export default function InventoryView({
|
||||
[locationsQ.data],
|
||||
);
|
||||
|
||||
// The admin catalogue (imports, curation) is scoped to this tenant's own
|
||||
// The admin catalogue (imports, curation) reads through this tenant's own
|
||||
// primary/hub outlet — not the hardcoded Fiesta defaults, so onboarded
|
||||
// tenants other than Fiesta see their own imports reflected here.
|
||||
//
|
||||
// READS are scoped here; PRICE WRITES are not. A price the admin sets is a
|
||||
// business-level decision that has to reach every branch, so it goes out to
|
||||
// `allLocationIds` via priceEverywhere() below. Writing only to this outlet
|
||||
// left every other branch at ₹0.
|
||||
const primaryLocationId = locations[0]?.locationid ?? FIESTA_PRIMARY_LOCATION_ID;
|
||||
const allLocationIds = useMemo(
|
||||
() => (locations.length ? locations.map((l) => l.locationid) : [primaryLocationId]),
|
||||
[locations, primaryLocationId],
|
||||
);
|
||||
|
||||
const storesStock = useFiestaStoresStock(
|
||||
tenantId,
|
||||
@@ -125,6 +134,7 @@ export default function InventoryView({
|
||||
const [hoveredAdminProduct, setHoveredAdminProduct] = useState<ProductMatrixItem | null>(null);
|
||||
const [localSearch, setLocalSearch] = useState('');
|
||||
const storeCat = useStoreCatalogue(tenantId, primaryLocationId);
|
||||
const { priceEverywhere, isPending: isPricing } = usePriceEverywhere(tenantId);
|
||||
const [importPrice, setImportPrice] = useState<string>('');
|
||||
const [isSettingPrice, setIsSettingPrice] = useState(false);
|
||||
const [addingPriceProdId, setAddingPriceProdId] = useState<string | null>(null);
|
||||
@@ -619,7 +629,12 @@ export default function InventoryView({
|
||||
</button>
|
||||
) : addingPriceProdId === prod.id ? (
|
||||
<div className="flex flex-col gap-1.5 p-2 bg-slate-50 border border-slate-200 rounded-none" onClick={(e) => e.stopPropagation()}>
|
||||
<label className="text-[9px] font-extrabold text-slate-600 uppercase tracking-widest text-center">Set Selling Price (₹)</label>
|
||||
<label className="text-[9px] font-extrabold text-slate-600 uppercase tracking-widest text-center">
|
||||
Set Selling Price (₹)
|
||||
</label>
|
||||
<span className="text-[8px] font-bold text-slate-400 uppercase tracking-wider text-center -mt-1">
|
||||
{allLocationIds.length === 1 ? 'Your store' : `All ${allLocationIds.length} stores`}
|
||||
</span>
|
||||
<input
|
||||
type="number"
|
||||
value={cardImportPrice}
|
||||
@@ -631,15 +646,17 @@ export default function InventoryView({
|
||||
<div className="flex gap-1">
|
||||
<button onClick={(e) => { e.stopPropagation(); setAddingPriceProdId(null); }} className="flex-1 py-1 bg-white text-slate-500 border border-slate-200 text-[9px] font-bold">Cancel</button>
|
||||
<button
|
||||
disabled={!cardImportPrice || Number(cardImportPrice) <= 0}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// qty: 0 — publishing to the catalogue is a listing/pricing action,
|
||||
// not a stock delivery. Real quantity only lands via the
|
||||
// request → approve → Mark as Received flow.
|
||||
storeCat.add({ productid: prod.id, name: prod.name, image: prod.image, category: prod.category, sku: prod.sku, price: Number(cardImportPrice), unit: prod.exposure, qty: 0, status: 'Active' });
|
||||
setAddingPriceProdId(null);
|
||||
}}
|
||||
disabled={!cardImportPrice || Number(cardImportPrice) <= 0 || isPricing}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// Publishing to the catalogue is a listing/pricing action, not a
|
||||
// stock delivery — priceEverywhere() writes quantity 0 at every
|
||||
// outlet. Real quantity only lands via the request → approve →
|
||||
// Mark as Received flow.
|
||||
priceEverywhere(prod.id, Number(cardImportPrice), allLocationIds, {
|
||||
onSuccess: () => setAddingPriceProdId(null),
|
||||
});
|
||||
}}
|
||||
className="flex-1 py-1 bg-[#662582] text-white text-[9px] font-bold disabled:opacity-50"
|
||||
>
|
||||
Confirm
|
||||
@@ -968,30 +985,26 @@ export default function InventoryView({
|
||||
</div>
|
||||
|
||||
<button
|
||||
disabled={!importPrice || Number(importPrice) <= 0}
|
||||
disabled={!importPrice || Number(importPrice) <= 0 || isPricing}
|
||||
onClick={() => {
|
||||
// First-time publish gets qty 0 — it's a listing/pricing action, not a
|
||||
// stock delivery; real quantity only lands via Mark as Received. If this
|
||||
// product is already published, preserve its existing qty so a mere price
|
||||
// edit here doesn't zero out stock that's since been received.
|
||||
const existingQty = storeCat.items.find(i => i.productid === selectedAdminProduct.id)?.qty ?? 0;
|
||||
storeCat.add({
|
||||
productid: selectedAdminProduct.id,
|
||||
name: selectedAdminProduct.name,
|
||||
image: selectedAdminProduct.image,
|
||||
category: selectedAdminProduct.category,
|
||||
sku: selectedAdminProduct.sku,
|
||||
price: Number(importPrice),
|
||||
unit: selectedAdminProduct.exposure || 'Pc',
|
||||
qty: existingQty,
|
||||
status: 'Active'
|
||||
});
|
||||
// Publishing is a listing/pricing action, never a stock delivery — real
|
||||
// quantity only lands via Mark as Received. priceEverywhere() sends
|
||||
// quantity 0, and because the upsert doesn't update quantity, an outlet
|
||||
// that already holds stock keeps it while a new outlet starts at zero.
|
||||
priceEverywhere(selectedAdminProduct.id, Number(importPrice), allLocationIds);
|
||||
}}
|
||||
className="w-full flex items-center justify-center gap-2 py-3 rounded-xl text-xs font-extrabold transition-all bg-[#662582] hover:bg-[#531e6a] text-white shadow-md disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
|
||||
>
|
||||
<CheckCircle size={16} strokeWidth={2.5} />
|
||||
{storeCat.has(selectedAdminProduct.id) ? 'Save Price to Database' : 'Save Price & Publish to Store'}
|
||||
{isPricing
|
||||
? 'Saving…'
|
||||
: storeCat.has(selectedAdminProduct.id)
|
||||
? 'Save Price to All Stores'
|
||||
: 'Save Price & Publish to All Stores'}
|
||||
</button>
|
||||
<p className="text-[10px] font-semibold text-slate-500 text-center -mt-1">
|
||||
Applies to {allLocationIds.length === 1 ? 'your store' : `all ${allLocationIds.length} stores`}
|
||||
</p>
|
||||
|
||||
{storeCat.has(selectedAdminProduct.id) && (
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user