fix : 모델, BOM 구성 수정
- 설계용 모델, BOM 기능 추가
This commit is contained in:
221
app/Services/Design/BomTemplateService.php
Normal file
221
app/Services/Design/BomTemplateService.php
Normal file
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Design;
|
||||
|
||||
use App\Models\Design\BomTemplate;
|
||||
use App\Models\Design\BomTemplateItem;
|
||||
use App\Models\Design\ModelVersion;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class BomTemplateService extends Service
|
||||
{
|
||||
/** 페이징 목록(옵션: 모델버전 필터) */
|
||||
public function paginate(?int $modelVersionId, int $page = 1, int $size = 20): LengthAwarePaginator
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$q = BomTemplate::query()->where('tenant_id', $tenantId);
|
||||
|
||||
if ($modelVersionId) {
|
||||
$q->where('model_version_id', $modelVersionId);
|
||||
}
|
||||
|
||||
return $q->orderByDesc('is_primary')
|
||||
->orderBy('name')
|
||||
->paginate($size, ['*'], 'page', $page);
|
||||
}
|
||||
|
||||
/** 템플릿 upsert(name 기준) */
|
||||
public function upsertTemplate(int $modelVersionId, string $name = 'Main', bool $isPrimary = true, ?string $notes = null): BomTemplate
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$mv = ModelVersion::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->find($modelVersionId);
|
||||
|
||||
if (!$mv) {
|
||||
throw new NotFoundHttpException(__('error.not_found'));
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($tenantId, $mv, $name, $isPrimary, $notes) {
|
||||
$tpl = BomTemplate::query()
|
||||
->where('model_version_id', $mv->id)
|
||||
->where('name', $name)
|
||||
->first();
|
||||
|
||||
if (!$tpl) {
|
||||
$tpl = BomTemplate::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'model_version_id' => $mv->id,
|
||||
'name' => $name,
|
||||
'is_primary' => $isPrimary,
|
||||
'notes' => $notes,
|
||||
]);
|
||||
} else {
|
||||
$tpl->fill(['is_primary' => $isPrimary, 'notes' => $notes])->save();
|
||||
}
|
||||
|
||||
if ($isPrimary) {
|
||||
BomTemplate::query()
|
||||
->where('model_version_id', $mv->id)
|
||||
->where('id', '<>', $tpl->id)
|
||||
->update(['is_primary' => false]);
|
||||
}
|
||||
|
||||
return $tpl;
|
||||
});
|
||||
}
|
||||
|
||||
/** 템플릿 메타 수정 */
|
||||
public function updateTemplate(int $templateId, array $data): BomTemplate
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$tpl = BomTemplate::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->find($templateId);
|
||||
|
||||
if (!$tpl) {
|
||||
throw new NotFoundHttpException(__('error.not_found'));
|
||||
}
|
||||
|
||||
$name = $data['name'] ?? $tpl->name;
|
||||
if ($name !== $tpl->name) {
|
||||
$dup = BomTemplate::query()
|
||||
->where('model_version_id', $tpl->model_version_id)
|
||||
->where('name', $name)
|
||||
->where('id', '<>', $tpl->id)
|
||||
->exists();
|
||||
if ($dup) {
|
||||
throw ValidationException::withMessages(['name' => __('error.duplicate')]);
|
||||
}
|
||||
}
|
||||
|
||||
$tpl->fill($data)->save();
|
||||
|
||||
if (array_key_exists('is_primary', $data) && $data['is_primary']) {
|
||||
// 다른 템플릿 대표 해제
|
||||
BomTemplate::query()
|
||||
->where('model_version_id', $tpl->model_version_id)
|
||||
->where('id', '<>', $tpl->id)
|
||||
->update(['is_primary' => false]);
|
||||
}
|
||||
|
||||
return $tpl;
|
||||
}
|
||||
|
||||
/** 템플릿 삭제(soft) */
|
||||
public function deleteTemplate(int $templateId): void
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$tpl = BomTemplate::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->find($templateId);
|
||||
|
||||
if (!$tpl) {
|
||||
throw new NotFoundHttpException(__('error.not_found'));
|
||||
}
|
||||
|
||||
$tpl->delete();
|
||||
}
|
||||
|
||||
/** 템플릿 상세 (옵션: 항목 포함) */
|
||||
public function show(int $templateId, bool $withItems = false): BomTemplate
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$q = BomTemplate::query()->where('tenant_id', $tenantId);
|
||||
if ($withItems) {
|
||||
$q->with(['items' => fn($w) => $w->orderBy('sort_order')]);
|
||||
}
|
||||
|
||||
$tpl = $q->find($templateId);
|
||||
if (!$tpl) {
|
||||
throw new NotFoundHttpException(__('error.not_found'));
|
||||
}
|
||||
|
||||
return $tpl;
|
||||
}
|
||||
|
||||
/** 항목 일괄 치환 */
|
||||
public function replaceItems(int $templateId, array $items): void
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$tpl = BomTemplate::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->find($templateId);
|
||||
|
||||
if (!$tpl) {
|
||||
throw new NotFoundHttpException(__('error.not_found'));
|
||||
}
|
||||
|
||||
// 1차 검증
|
||||
foreach ($items as $i => $row) {
|
||||
$refType = strtoupper((string) Arr::get($row, 'ref_type'));
|
||||
$qty = (float) Arr::get($row, 'qty', 0);
|
||||
if (!in_array($refType, ['MATERIAL','PRODUCT'], true)) {
|
||||
throw ValidationException::withMessages(["items.$i.ref_type" => __('error.validation_failed')]);
|
||||
}
|
||||
if ($qty <= 0) {
|
||||
throw ValidationException::withMessages(["items.$i.qty" => __('error.validation_failed')]);
|
||||
}
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($tenantId, $tpl, $items) {
|
||||
BomTemplateItem::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('bom_template_id', $tpl->id)
|
||||
->delete();
|
||||
|
||||
$now = now();
|
||||
$payloads = [];
|
||||
foreach ($items as $row) {
|
||||
$payloads[] = [
|
||||
'tenant_id' => $tenantId,
|
||||
'bom_template_id' => $tpl->id,
|
||||
'ref_type' => strtoupper($row['ref_type']),
|
||||
'ref_id' => (int) $row['ref_id'],
|
||||
'qty' => (string) ($row['qty'] ?? 1),
|
||||
'waste_rate' => (string) ($row['waste_rate'] ?? 0),
|
||||
'uom_id' => $row['uom_id'] ?? null,
|
||||
'notes' => $row['notes'] ?? null,
|
||||
'sort_order' => (int) ($row['sort_order'] ?? 0),
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($payloads)) {
|
||||
BomTemplateItem::insert($payloads);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 항목 조회 */
|
||||
public function listItems(int $templateId)
|
||||
{
|
||||
$tenantId = $this->tenantId();
|
||||
|
||||
$tpl = BomTemplate::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->find($templateId);
|
||||
|
||||
if (!$tpl) {
|
||||
throw new NotFoundHttpException(__('error.not_found'));
|
||||
}
|
||||
|
||||
return BomTemplateItem::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('bom_template_id', $tpl->id)
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user