diff --git a/app/Http/Controllers/Barobill/EtaxController.php b/app/Http/Controllers/Barobill/EtaxController.php index 8fc60f55..29aadccc 100644 --- a/app/Http/Controllers/Barobill/EtaxController.php +++ b/app/Http/Controllers/Barobill/EtaxController.php @@ -339,6 +339,82 @@ public function sendToNts(Request $request): JsonResponse } } + /** + * 공급자 기초정보 조회 + */ + public function getSupplier(): JsonResponse + { + $tenantId = session('selected_tenant_id'); + if (!$tenantId) { + return response()->json(['success' => false, 'error' => '테넌트가 선택되지 않았습니다.'], 400); + } + + $member = BarobillMember::where('tenant_id', $tenantId)->first(); + if (!$member) { + return response()->json(['success' => false, 'error' => '바로빌 회원사 정보가 없습니다.'], 404); + } + + return response()->json([ + 'success' => true, + 'supplier' => [ + 'bizno' => $member->biz_no, + 'name' => $member->corp_name ?? '', + 'ceo' => $member->ceo_name ?? '', + 'addr' => $member->addr ?? '', + 'bizType' => $member->biz_type ?? '', + 'bizClass' => $member->biz_class ?? '', + 'contact' => $member->manager_name ?? '', + 'contactPhone' => $member->manager_hp ?? '', + 'email' => $member->manager_email ?? '', + ], + ]); + } + + /** + * 공급자 기초정보 수정 + */ + public function updateSupplier(Request $request): JsonResponse + { + $tenantId = session('selected_tenant_id'); + if (!$tenantId) { + return response()->json(['success' => false, 'error' => '테넌트가 선택되지 않았습니다.'], 400); + } + + $member = BarobillMember::where('tenant_id', $tenantId)->first(); + if (!$member) { + return response()->json(['success' => false, 'error' => '바로빌 회원사 정보가 없습니다.'], 404); + } + + $validated = $request->validate([ + 'corp_name' => 'required|string|max:100', + 'ceo_name' => 'required|string|max:50', + 'addr' => 'nullable|string|max:255', + 'biz_type' => 'nullable|string|max:100', + 'biz_class' => 'nullable|string|max:100', + 'manager_name' => 'nullable|string|max:50', + 'manager_email' => 'nullable|email|max:100', + 'manager_hp' => 'nullable|string|max:20', + ]); + + $member->update($validated); + + return response()->json([ + 'success' => true, + 'message' => '공급자 정보가 수정되었습니다.', + 'supplier' => [ + 'bizno' => $member->biz_no, + 'name' => $member->corp_name ?? '', + 'ceo' => $member->ceo_name ?? '', + 'addr' => $member->addr ?? '', + 'bizType' => $member->biz_type ?? '', + 'bizClass' => $member->biz_class ?? '', + 'contact' => $member->manager_name ?? '', + 'contactPhone' => $member->manager_hp ?? '', + 'email' => $member->manager_email ?? '', + ], + ]); + } + /** * 세금계산서 삭제 */ diff --git a/resources/views/barobill/etax/index.blade.php b/resources/views/barobill/etax/index.blade.php index 7f3b373a..fc6c6e04 100644 --- a/resources/views/barobill/etax/index.blade.php +++ b/resources/views/barobill/etax/index.blade.php @@ -72,6 +72,8 @@ issue: '{{ route("barobill.etax.issue") }}', sendToNts: '{{ route("barobill.etax.send-to-nts") }}', delete: '{{ route("barobill.etax.delete") }}', + supplier: '{{ route("barobill.etax.supplier") }}', + supplierUpdate: '{{ route("barobill.etax.supplier.update") }}', }; const CSRF_TOKEN = '{{ csrf_token() }}'; @@ -85,7 +87,7 @@ ]; // 공급자 정보 (현재 테넌트의 바로빌 회원사 정보) - const FIXED_SUPPLIER = { + const INITIAL_SUPPLIER = { bizno: '{{ $barobillMember?->biz_no ?? "" }}', name: '{{ $barobillMember?->corp_name ?? $currentTenant?->company_name ?? "" }}', ceo: '{{ $barobillMember?->ceo_name ?? $currentTenant?->ceo_name ?? "" }}', @@ -93,7 +95,7 @@ bizType: '{{ $barobillMember?->biz_type ?? "" }}', bizClass: '{{ $barobillMember?->biz_class ?? "" }}', contact: '{{ $barobillMember?->manager_name ?? "" }}', - contactPhone: '{{ $barobillMember?->manager_phone ?? "" }}', + contactPhone: '{{ $barobillMember?->manager_hp ?? "" }}', email: '{{ $barobillMember?->manager_email ?? $currentTenant?->email ?? "" }}' }; @@ -130,7 +132,7 @@ ); // IssueForm Component - const IssueForm = ({ onIssue, onCancel }) => { + const IssueForm = ({ onIssue, onCancel, supplier }) => { const generateRandomData = () => { let items = []; let supplyDate; @@ -183,15 +185,15 @@ } return { - supplierBizno: FIXED_SUPPLIER.bizno, - supplierName: FIXED_SUPPLIER.name, - supplierCeo: FIXED_SUPPLIER.ceo, - supplierAddr: FIXED_SUPPLIER.addr, - supplierBizType: FIXED_SUPPLIER.bizType, - supplierBizClass: FIXED_SUPPLIER.bizClass, - supplierContact: FIXED_SUPPLIER.contact, - supplierContactPhone: FIXED_SUPPLIER.contactPhone, - supplierEmail: FIXED_SUPPLIER.email, + supplierBizno: supplier.bizno, + supplierName: supplier.name, + supplierCeo: supplier.ceo, + supplierAddr: supplier.addr, + supplierBizType: supplier.bizType, + supplierBizClass: supplier.bizClass, + supplierContact: supplier.contact, + supplierContactPhone: supplier.contactPhone, + supplierEmail: supplier.email, ...recipientData, supplyDate: formatLocalDate(supplyDate), items, @@ -808,6 +810,127 @@ className="px-3 py-1.5 text-sm bg-stone-100 text-stone-600 rounded-lg hover:bg-s ); }; + // SupplierSettingsModal Component + const SupplierSettingsModal = ({ supplier, onClose, onSaved }) => { + const [form, setForm] = useState({ + corp_name: supplier.name || '', + ceo_name: supplier.ceo || '', + addr: supplier.addr || '', + biz_type: supplier.bizType || '', + biz_class: supplier.bizClass || '', + manager_name: supplier.contact || '', + manager_hp: supplier.contactPhone || '', + manager_email: supplier.email || '', + }); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(''); + + const handleChange = (field, value) => { + setForm(prev => ({ ...prev, [field]: value })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setSaving(true); + setError(''); + try { + const response = await fetch(API.supplierUpdate, { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF_TOKEN }, + body: JSON.stringify(form), + }); + const result = await response.json(); + if (result.success) { + onSaved(result.supplier); + onClose(); + } else { + setError(result.error || '저장에 실패했습니다.'); + } + } catch (err) { + setError('저장 중 오류가 발생했습니다: ' + err.message); + } finally { + setSaving(false); + } + }; + + const labelClass = "block text-sm font-medium text-stone-700 mb-1"; + const inputClass = "w-full rounded-lg border border-stone-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none"; + const readonlyClass = "w-full rounded-lg border border-stone-200 bg-stone-50 px-3 py-2 text-sm text-stone-500"; + + return ( +
세금계산서 발행 시 사용되는 공급자 정보입니다
+