Compare commits
No commits in common. "d2ddc5e6b79aba56a31cd0668f476bb0a4c6e74b" and "bd51f9abe027b27a15d6ae04e81847e5eafdff43" have entirely different histories.
d2ddc5e6b7
...
bd51f9abe0
@ -138,8 +138,7 @@ public function rawMaterialCatalog(?Cutting $cutting = null, ?User $user = null)
|
|||||||
->with([
|
->with([
|
||||||
'prices' => fn ($query) => $query
|
'prices' => fn ($query) => $query
|
||||||
->orderBy('created_at')
|
->orderBy('created_at')
|
||||||
->with('media')
|
->with('media'),
|
||||||
->where(fn ($q) => $q->where('stock', '>', 0)->orWhereIn('id', $selectedPriceIds)),
|
|
||||||
])
|
])
|
||||||
->where(function (Builder $query) use ($selectedPriceIds): void {
|
->where(function (Builder $query) use ($selectedPriceIds): void {
|
||||||
$query->active();
|
$query->active();
|
||||||
|
|||||||
@ -158,13 +158,7 @@ public function catalogItems(?Order $order = null, ?User $user = null): Collecti
|
|||||||
->with([
|
->with([
|
||||||
'variants' => fn ($query) => $query
|
'variants' => fn ($query) => $query
|
||||||
->with('media')
|
->with('media')
|
||||||
->orderBy('created_at')
|
->orderBy('created_at'),
|
||||||
->where(fn ($q) => $q
|
|
||||||
->where('stock', '>', 0)
|
|
||||||
->orWhere('reject_stock', '>', 0)
|
|
||||||
->orWhere('retail_stock', '>', 0)
|
|
||||||
->orWhereIn('id', $orderVariantIds)
|
|
||||||
),
|
|
||||||
])
|
])
|
||||||
->where(function (Builder $query) use ($orderVariantIds): void {
|
->where(function (Builder $query) use ($orderVariantIds): void {
|
||||||
$query->active();
|
$query->active();
|
||||||
@ -863,13 +857,13 @@ private function presentVariantPricesFromCollection(int $variantId, Collection $
|
|||||||
return $allPricesByVariant
|
return $allPricesByVariant
|
||||||
->get($variantId, collect())
|
->get($variantId, collect())
|
||||||
->map(fn ($price) => [
|
->map(fn ($price) => [
|
||||||
'type' => is_array($price) ? $price['price_type'] : $price->price_type->value,
|
'type' => $price->price_type->value,
|
||||||
'type_label' => PriceType::from(is_array($price) ? $price['price_type'] : $price->price_type->value)->label(),
|
'type_label' => $price->price_type->label(),
|
||||||
'price' => (int) (is_array($price) ? $price['price'] : $price->price),
|
'price' => (int) $price->price,
|
||||||
'price_formatted' => is_array($price) ? $price['price_formatted'] : $price->price_formatted,
|
'price_formatted' => $price->price_formatted,
|
||||||
'price_input' => (string) (is_array($price) ? $price['price'] : $price->price),
|
'price_input' => (string) $price->price,
|
||||||
'cost_per_unit' => (int) (is_array($price) ? ($price['cost_per_unit'] ?? $price['price']) : $price->cost_per_unit),
|
'cost_per_unit' => (int) $price->cost_per_unit,
|
||||||
'cost_per_unit_formatted' => is_array($price) ? ($price['cost_per_unit_formatted'] ?? $price['price_formatted']) : $price->cost_per_unit_formatted,
|
'cost_per_unit_formatted' => $price->cost_per_unit_formatted,
|
||||||
])
|
])
|
||||||
->values()
|
->values()
|
||||||
->all();
|
->all();
|
||||||
|
|||||||
17
public/sw.js
17
public/sw.js
@ -11,15 +11,14 @@ self.addEventListener('install', () => {
|
|||||||
// ── Activate ─────────────────────────────────────────────────────────────────
|
// ── Activate ─────────────────────────────────────────────────────────────────
|
||||||
self.addEventListener('activate', (event) => {
|
self.addEventListener('activate', (event) => {
|
||||||
event.waitUntil(
|
event.waitUntil(
|
||||||
(async () => {
|
caches
|
||||||
const keys = await caches.keys();
|
.keys()
|
||||||
await Promise.all(keys.map((k) => caches.delete(k)));
|
.then((keys) =>
|
||||||
|
Promise.all(
|
||||||
await self.registration.unregister();
|
keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)),
|
||||||
|
),
|
||||||
const clientList = await self.clients.matchAll({ type: 'window' });
|
)
|
||||||
clientList.forEach((client) => client.navigate(client.url));
|
.then(() => self.clients.claim()),
|
||||||
})(),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -77,9 +77,8 @@ function openCombination(rawMaterial: CuttingRawMaterialCatalogItem, price: Cutt
|
|||||||
</Empty>
|
</Empty>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="columns-1 gap-4 sm:columns-2 xl:columns-3">
|
<div v-else class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
<template v-for="rawMaterial in filteredRawMaterials" :key="rawMaterial.id">
|
<PosCatalogCard v-for="rawMaterial in filteredRawMaterials" :key="rawMaterial.id"
|
||||||
<PosCatalogCard v-if="rawMaterial.prices.length > 0"
|
|
||||||
:title="rawMaterial.name" :cover-image="getFirstCoverImage(rawMaterial.prices)">
|
:title="rawMaterial.name" :cover-image="getFirstCoverImage(rawMaterial.prices)">
|
||||||
<template #header-extra>
|
<template #header-extra>
|
||||||
<Badge variant="secondary" class="mt-1.5">
|
<Badge variant="secondary" class="mt-1.5">
|
||||||
@ -149,7 +148,6 @@ function openCombination(rawMaterial: CuttingRawMaterialCatalogItem, price: Cutt
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PosCatalogCard>
|
</PosCatalogCard>
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@ -85,12 +85,15 @@ const emit = defineEmits<{
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="columns-1 gap-4 sm:columns-2 xl:columns-3">
|
<div v-else class="columns-1 gap-4 sm:columns-2 xl:columns-3">
|
||||||
<template v-for="product in filteredCatalog" :key="product.id">
|
|
||||||
<PosCatalogCard
|
<PosCatalogCard
|
||||||
v-if="product.variants.length > 0"
|
v-for="product in filteredCatalog"
|
||||||
|
:key="product.id"
|
||||||
:title="product.name"
|
:title="product.name"
|
||||||
:cover-image="getFirstCoverImage(product.variants)"
|
:cover-image="getFirstCoverImage(product.variants)"
|
||||||
>
|
>
|
||||||
|
<p v-if="!product.variants.length" class="px-3 py-4 text-sm text-muted-foreground">
|
||||||
|
Belum ada varian
|
||||||
|
</p>
|
||||||
<div
|
<div
|
||||||
v-for="variant in product.variants"
|
v-for="variant in product.variants"
|
||||||
:key="variant.id"
|
:key="variant.id"
|
||||||
@ -144,7 +147,6 @@ const emit = defineEmits<{
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</PosCatalogCard>
|
</PosCatalogCard>
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@ -125,7 +125,6 @@ function openVerificationDetail(requestId: number | undefined) {
|
|||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead class="w-10">#</TableHead>
|
|
||||||
<TableHead>Varian</TableHead>
|
<TableHead>Varian</TableHead>
|
||||||
<TableHead>Foto</TableHead>
|
<TableHead>Foto</TableHead>
|
||||||
<TableHead>Stok Bagus</TableHead>
|
<TableHead>Stok Bagus</TableHead>
|
||||||
@ -140,10 +139,7 @@ function openVerificationDetail(requestId: number | undefined) {
|
|||||||
Belum ada varian
|
Belum ada varian
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow v-for="(variant, variantIndex) in product.variants" :key="variant.id">
|
<TableRow v-for="variant in product.variants" :key="variant.id">
|
||||||
<TableCell class="text-muted-foreground tabular-nums text-center">
|
|
||||||
{{ variantIndex + 1 }}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell class="font-medium">
|
<TableCell class="font-medium">
|
||||||
{{ variant.name }}
|
{{ variant.name }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@ -109,7 +109,6 @@ function openVerificationDetail(requestId: number | undefined) {
|
|||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead class="w-10">#</TableHead>
|
|
||||||
<TableHead>Varian</TableHead>
|
<TableHead>Varian</TableHead>
|
||||||
<TableHead>Foto</TableHead>
|
<TableHead>Foto</TableHead>
|
||||||
<TableHead>Stok</TableHead>
|
<TableHead>Stok</TableHead>
|
||||||
@ -118,14 +117,11 @@ function openVerificationDetail(requestId: number | undefined) {
|
|||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<TableRow v-if="!material.prices.length" :key="`${material.id}-empty`">
|
<TableRow v-if="!material.prices.length" :key="`${material.id}-empty`">
|
||||||
<TableCell colspan="5" class="text-muted-foreground">
|
<TableCell colspan="4" class="text-muted-foreground">
|
||||||
Belum ada varian
|
Belum ada varian
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow v-for="(price, priceIndex) in material.prices" :key="price.id">
|
<TableRow v-for="price in material.prices" :key="price.id">
|
||||||
<TableCell class="text-muted-foreground tabular-nums text-center">
|
|
||||||
{{ priceIndex + 1 }}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell class="font-medium">
|
<TableCell class="font-medium">
|
||||||
{{ price.variant }}
|
{{ price.variant }}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@ -1122,49 +1122,3 @@ function setupDraftItems(User $user): array
|
|||||||
expect($combination->material_result)->toBe(15);
|
expect($combination->material_result)->toBe(15);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─── Raw Material Catalog Stock Filter ───────────────────────
|
|
||||||
|
|
||||||
describe('Raw Material Catalog Stock Filter', function () {
|
|
||||||
test('rawMaterialCatalog excludes variants with zero stock', function () {
|
|
||||||
$user = createCuttingUserWithPermission(PermissionEnum::CUTTINGS_VIEW, PermissionEnum::CUTTINGS_CREATE);
|
|
||||||
|
|
||||||
$rawMaterial = RawMaterial::factory()->create(['is_active' => true]);
|
|
||||||
$priceWithStock = RawMaterialPrice::factory()->create(['raw_material_id' => $rawMaterial->id, 'stock' => 10]);
|
|
||||||
$priceWithoutStock = RawMaterialPrice::factory()->create(['raw_material_id' => $rawMaterial->id, 'stock' => 0]);
|
|
||||||
|
|
||||||
$this->actingAs($user)
|
|
||||||
->get(route('admin.manage.cuttings.create'))
|
|
||||||
->assertOk();
|
|
||||||
|
|
||||||
$service = app(\App\Services\Manage\CuttingService::class);
|
|
||||||
$catalog = $service->rawMaterialCatalog(user: $user);
|
|
||||||
$material = $catalog->firstWhere('id', $rawMaterial->id);
|
|
||||||
|
|
||||||
$priceIds = $material->prices->pluck('id')->all();
|
|
||||||
|
|
||||||
expect($priceIds)->toContain($priceWithStock->id);
|
|
||||||
expect($priceIds)->not->toContain($priceWithoutStock->id);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('rawMaterialCatalog includes zero-stock variants already used in cutting', function () {
|
|
||||||
$user = createCuttingUserWithPermission(PermissionEnum::CUTTINGS_VIEW, PermissionEnum::CUTTINGS_UPDATE);
|
|
||||||
|
|
||||||
$rawMaterial = RawMaterial::factory()->create(['is_active' => true]);
|
|
||||||
$priceZeroStock = RawMaterialPrice::factory()->create(['raw_material_id' => $rawMaterial->id, 'stock' => 0]);
|
|
||||||
|
|
||||||
$cutting = Cutting::factory()->create(['created_by_id' => $user->id]);
|
|
||||||
CuttingMaterial::factory()->create([
|
|
||||||
'cutting_id' => $cutting->id,
|
|
||||||
'raw_material_price_id' => $priceZeroStock->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$service = app(\App\Services\Manage\CuttingService::class);
|
|
||||||
$catalog = $service->rawMaterialCatalog($cutting);
|
|
||||||
$material = $catalog->firstWhere('id', $rawMaterial->id);
|
|
||||||
|
|
||||||
$priceIds = $material?->prices->pluck('id')->all() ?? [];
|
|
||||||
|
|
||||||
expect($priceIds)->toContain($priceZeroStock->id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@ -613,55 +613,3 @@ function setupOrderDraftItems(User $user): ProductVariant
|
|||||||
expect($item->trashed())->toBeTrue();
|
expect($item->trashed())->toBeTrue();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─── Order Catalog Stock Filter ─────────────────────────────
|
|
||||||
|
|
||||||
describe('Order Catalog Stock Filter', function () {
|
|
||||||
test('catalogItems excludes variants where all stock types are zero', function () {
|
|
||||||
$user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_CREATE);
|
|
||||||
|
|
||||||
$product = Product::factory()->create(['is_active' => true]);
|
|
||||||
|
|
||||||
$variantWithStock = ProductVariant::factory()->create(['product_id' => $product->id, 'stock' => 10, 'reject_stock' => 0, 'retail_stock' => 0]);
|
|
||||||
$variantAllZero = ProductVariant::factory()->create(['product_id' => $product->id, 'stock' => 0, 'reject_stock' => 0, 'retail_stock' => 0]);
|
|
||||||
$variantRejectOnly = ProductVariant::factory()->create(['product_id' => $product->id, 'stock' => 0, 'reject_stock' => 5, 'retail_stock' => 0]);
|
|
||||||
$variantRetailOnly = ProductVariant::factory()->create(['product_id' => $product->id, 'stock' => 0, 'reject_stock' => 0, 'retail_stock' => 3]);
|
|
||||||
|
|
||||||
$service = app(\App\Services\Manage\OrderService::class);
|
|
||||||
$catalog = $service->catalogItems(user: $user);
|
|
||||||
$found = $catalog->firstWhere('id', $product->id);
|
|
||||||
|
|
||||||
$variantIds = $found?->variants->pluck('id')->all() ?? [];
|
|
||||||
|
|
||||||
expect($variantIds)->toContain($variantWithStock->id);
|
|
||||||
expect($variantIds)->toContain($variantRejectOnly->id);
|
|
||||||
expect($variantIds)->toContain($variantRetailOnly->id);
|
|
||||||
expect($variantIds)->not->toContain($variantAllZero->id);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('catalogItems includes all-zero-stock variants already in order', function () {
|
|
||||||
$user = createOrderUserWithPermission(PermissionEnum::ORDERS_VIEW, PermissionEnum::ORDERS_UPDATE);
|
|
||||||
|
|
||||||
$product = Product::factory()->create(['is_active' => true]);
|
|
||||||
$variantAllZero = ProductVariant::factory()->create([
|
|
||||||
'product_id' => $product->id,
|
|
||||||
'stock' => 0,
|
|
||||||
'reject_stock' => 0,
|
|
||||||
'retail_stock' => 0,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$order = Order::factory()->create(['created_by_id' => $user->id]);
|
|
||||||
OrderItem::factory()->create([
|
|
||||||
'order_id' => $order->id,
|
|
||||||
'product_variant_id' => $variantAllZero->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$service = app(\App\Services\Manage\OrderService::class);
|
|
||||||
$catalog = $service->catalogItems($order);
|
|
||||||
$found = $catalog->firstWhere('id', $product->id);
|
|
||||||
|
|
||||||
$variantIds = $found?->variants->pluck('id')->all() ?? [];
|
|
||||||
|
|
||||||
expect($variantIds)->toContain($variantAllZero->id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user