197 lines
5.9 KiB
PHP
197 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Pricing;
|
|
|
|
use App\Models\Orders\Client;
|
|
use App\Models\Products\PriceHistory;
|
|
use App\Services\Service;
|
|
use Carbon\Carbon;
|
|
|
|
class PricingService extends Service
|
|
{
|
|
/**
|
|
* 특정 항목(제품/자재)의 단가를 조회
|
|
*
|
|
* @param string $itemType 'PRODUCT' | 'MATERIAL'
|
|
* @param int $itemId 제품/자재 ID
|
|
* @param int|null $clientId 고객 ID (NULL이면 기본 가격)
|
|
* @param string|null $date 기준일 (NULL이면 오늘)
|
|
* @return array ['price' => float|null, 'price_history_id' => int|null, 'client_group_id' => int|null, 'warning' => string|null]
|
|
*/
|
|
public function getItemPrice(string $itemType, int $itemId, ?int $clientId = null, ?string $date = null): array
|
|
{
|
|
$date = $date ?? Carbon::today()->format('Y-m-d');
|
|
$clientGroupId = null;
|
|
|
|
// 1. 고객의 그룹 ID 확인
|
|
if ($clientId) {
|
|
$client = Client::where('tenant_id', $this->tenantId())
|
|
->where('id', $clientId)
|
|
->first();
|
|
|
|
if ($client) {
|
|
$clientGroupId = $client->client_group_id;
|
|
}
|
|
}
|
|
|
|
// 2. 가격 조회 (우선순위대로)
|
|
$priceHistory = null;
|
|
|
|
// 1순위: 고객 그룹별 매출단가
|
|
if ($clientGroupId) {
|
|
$priceHistory = $this->findPrice($itemType, $itemId, $clientGroupId, $date);
|
|
}
|
|
|
|
// 2순위: 기본 매출단가 (client_group_id = NULL)
|
|
if (! $priceHistory) {
|
|
$priceHistory = $this->findPrice($itemType, $itemId, null, $date);
|
|
}
|
|
|
|
// 3순위: NULL (경고)
|
|
if (! $priceHistory) {
|
|
return [
|
|
'price' => null,
|
|
'price_history_id' => null,
|
|
'client_group_id' => null,
|
|
'warning' => __('error.price_not_found', [
|
|
'item_type' => $itemType,
|
|
'item_id' => $itemId,
|
|
'date' => $date,
|
|
]),
|
|
];
|
|
}
|
|
|
|
return [
|
|
'price' => (float) $priceHistory->price,
|
|
'price_history_id' => $priceHistory->id,
|
|
'client_group_id' => $priceHistory->client_group_id,
|
|
'warning' => null,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 가격 이력에서 유효한 가격 조회
|
|
*/
|
|
private function findPrice(string $itemType, int $itemId, ?int $clientGroupId, string $date): ?PriceHistory
|
|
{
|
|
return PriceHistory::where('tenant_id', $this->tenantId())
|
|
->forItem($itemType, $itemId)
|
|
->forClientGroup($clientGroupId)
|
|
->salePrice()
|
|
->validAt($date)
|
|
->orderBy('started_at', 'desc')
|
|
->first();
|
|
}
|
|
|
|
/**
|
|
* 여러 항목의 단가를 일괄 조회
|
|
*
|
|
* @param array $items [['item_type' => 'PRODUCT', 'item_id' => 1], ...]
|
|
* @return array ['prices' => [...], 'warnings' => [...]]
|
|
*/
|
|
public function getBulkItemPrices(array $items, ?int $clientId = null, ?string $date = null): array
|
|
{
|
|
$prices = [];
|
|
$warnings = [];
|
|
|
|
foreach ($items as $item) {
|
|
$result = $this->getItemPrice(
|
|
$item['item_type'],
|
|
$item['item_id'],
|
|
$clientId,
|
|
$date
|
|
);
|
|
|
|
$prices[] = array_merge($item, [
|
|
'price' => $result['price'],
|
|
'price_history_id' => $result['price_history_id'],
|
|
'client_group_id' => $result['client_group_id'],
|
|
]);
|
|
|
|
if ($result['warning']) {
|
|
$warnings[] = $result['warning'];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'prices' => $prices,
|
|
'warnings' => $warnings,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 가격 등록/수정
|
|
*/
|
|
public function upsertPrice(array $data): PriceHistory
|
|
{
|
|
$data['tenant_id'] = $this->tenantId();
|
|
$data['created_by'] = $this->apiUserId();
|
|
$data['updated_by'] = $this->apiUserId();
|
|
|
|
// 중복 확인: 동일 조건(item, client_group, date 범위)의 가격이 이미 있는지
|
|
$existing = PriceHistory::where('tenant_id', $data['tenant_id'])
|
|
->where('item_type_code', $data['item_type_code'])
|
|
->where('item_id', $data['item_id'])
|
|
->where('price_type_code', $data['price_type_code'])
|
|
->where('client_group_id', $data['client_group_id'] ?? null)
|
|
->where('started_at', $data['started_at'])
|
|
->first();
|
|
|
|
if ($existing) {
|
|
$existing->update($data);
|
|
|
|
return $existing->fresh();
|
|
}
|
|
|
|
return PriceHistory::create($data);
|
|
}
|
|
|
|
/**
|
|
* 가격 이력 조회 (페이지네이션)
|
|
*
|
|
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
|
*/
|
|
public function listPrices(array $filters = [], int $perPage = 15)
|
|
{
|
|
$query = PriceHistory::where('tenant_id', $this->tenantId());
|
|
|
|
if (isset($filters['item_type_code'])) {
|
|
$query->where('item_type_code', $filters['item_type_code']);
|
|
}
|
|
|
|
if (isset($filters['item_id'])) {
|
|
$query->where('item_id', $filters['item_id']);
|
|
}
|
|
|
|
if (isset($filters['price_type_code'])) {
|
|
$query->where('price_type_code', $filters['price_type_code']);
|
|
}
|
|
|
|
if (isset($filters['client_group_id'])) {
|
|
$query->where('client_group_id', $filters['client_group_id']);
|
|
}
|
|
|
|
if (isset($filters['date'])) {
|
|
$query->validAt($filters['date']);
|
|
}
|
|
|
|
return $query->orderBy('started_at', 'desc')
|
|
->orderBy('created_at', 'desc')
|
|
->paginate($perPage);
|
|
}
|
|
|
|
/**
|
|
* 가격 삭제 (Soft Delete)
|
|
*/
|
|
public function deletePrice(int $id): bool
|
|
{
|
|
$price = PriceHistory::where('tenant_id', $this->tenantId())
|
|
->findOrFail($id);
|
|
|
|
$price->deleted_by = $this->apiUserId();
|
|
$price->save();
|
|
|
|
return $price->delete();
|
|
}
|
|
}
|