feat: add prices relationship to ProductVariant model and update related services and UI for improved product pricing management
This commit is contained in:
parent
ab03d5f0b9
commit
256b13ad73
45
app/Models/ProductPrice.php
Normal file
45
app/Models/ProductPrice.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\PriceType;
|
||||
use Illuminate\Database\Eloquent\Attributes\Appends;
|
||||
use Illuminate\Database\Eloquent\Attributes\Guarded;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
#[Guarded(['id'])]
|
||||
#[Appends(['price_formatted', 'price_input'])]
|
||||
class ProductPrice extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'type' => PriceType::class,
|
||||
'price' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
public function variant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ProductVariant::class, 'variant_id');
|
||||
}
|
||||
|
||||
public function priceFormatted(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => 'Rp '.number_format($this->price, 0, ',', '.'),
|
||||
);
|
||||
}
|
||||
|
||||
public function priceInput(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => (string) $this->price,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -42,6 +42,11 @@ public function cuttingResultPrices(): HasMany
|
||||
return $this->hasMany(CuttingResultPrice::class);
|
||||
}
|
||||
|
||||
public function prices(): HasMany
|
||||
{
|
||||
return $this->hasMany(ProductPrice::class, 'variant_id');
|
||||
}
|
||||
|
||||
public function product(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Product::class);
|
||||
|
||||
@ -27,7 +27,7 @@ public function paginateForIndex(array $tableQuery, string $isActive, string $ca
|
||||
->with([
|
||||
'categories',
|
||||
'variants' => fn ($query) => $query
|
||||
->with('media')
|
||||
->with(['media', 'prices' => fn ($query) => $query->orderBy('type')])
|
||||
->orderBy('created_at'),
|
||||
])
|
||||
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\PriceType;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('product_prices', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->foreignId('variant_id')->constrained('product_variants')->cascadeOnDelete();
|
||||
|
||||
$table->enum('type', array_column(PriceType::cases(), 'value'));
|
||||
$table->unsignedInteger('price');
|
||||
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
|
||||
$table->unique(['variant_id', 'type']);
|
||||
$table->index('variant_id');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('product_prices');
|
||||
}
|
||||
};
|
||||
@ -2,6 +2,7 @@
|
||||
import { Link } from '@inertiajs/vue3';
|
||||
import { computed } from 'vue';
|
||||
import DataTableToolbar from '@/components/data-table/DataTableToolbar.vue';
|
||||
import { formatRupiah } from '@/lib/rupiah';
|
||||
import MediaThumbnailCell from '@/components/media/MediaThumbnailCell.vue';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@ -103,6 +104,11 @@ function rowNumber(index: number): number {
|
||||
{{ product.variants.reduce((acc, v) => acc + v.stock, 0) }}
|
||||
</strong>
|
||||
</span>
|
||||
<span>
|
||||
Total harga <strong class="text-primary"> Rp
|
||||
{{ formatRupiah(product.variants.reduce((acc, v) => acc + (v.prices?.reduce((s, p) => s + p.price, 0) ?? 0), 0)) }}
|
||||
</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -154,7 +160,7 @@ function rowNumber(index: number): number {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else class="text-xs text-muted-foreground">Belum ada harga cutting</span>
|
||||
<span v-else class="text-xs text-muted-foreground">Belum ada harga</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
|
||||
@ -11,13 +11,12 @@ export type CategoryOption = {
|
||||
};
|
||||
|
||||
export type ProductPriceItem = {
|
||||
id: number;
|
||||
variant_id: number;
|
||||
type: string;
|
||||
type_label: string;
|
||||
price: number;
|
||||
price_formatted: string;
|
||||
price_input: string;
|
||||
cost_per_unit?: number;
|
||||
cost_per_unit_formatted?: string;
|
||||
};
|
||||
|
||||
export type ProductCategoryItem = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user