style: Pint 포맷팅 적용
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
class FixMenuUrlCommand extends Command
|
class FixMenuUrlCommand extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'menu:fix-url {name} {new-url} {--tenant=}';
|
protected $signature = 'menu:fix-url {name} {new-url} {--tenant=}';
|
||||||
|
|
||||||
protected $description = '메뉴 URL 수정 (이름으로 검색)';
|
protected $description = '메뉴 URL 수정 (이름으로 검색)';
|
||||||
|
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
@@ -26,6 +27,7 @@ public function handle(): int
|
|||||||
|
|
||||||
if ($menus->isEmpty()) {
|
if ($menus->isEmpty()) {
|
||||||
$this->error("'{$name}' 메뉴를 찾을 수 없습니다.");
|
$this->error("'{$name}' 메뉴를 찾을 수 없습니다.");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ public function handle(): int
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->info("{$menus->count()}건 수정 완료.");
|
$this->info("{$menus->count()}건 수정 완료.");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Http\Controllers\Api\Admin;
|
namespace App\Http\Controllers\Api\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Finance\BankAccount;
|
|
||||||
use App\Services\BankAccountService;
|
use App\Services\BankAccountService;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -86,7 +85,7 @@ public function store(Request $request): JsonResponse
|
|||||||
|
|
||||||
// 기본값 설정
|
// 기본값 설정
|
||||||
$validated['status'] = $validated['status'] ?? 'active';
|
$validated['status'] = $validated['status'] ?? 'active';
|
||||||
$validated['account_name'] = $validated['account_name'] ?? $validated['bank_name'] . ' 계좌';
|
$validated['account_name'] = $validated['account_name'] ?? $validated['bank_name'].' 계좌';
|
||||||
|
|
||||||
$account = $this->bankAccountService->createAccount($validated);
|
$account = $this->bankAccountService->createAccount($validated);
|
||||||
|
|
||||||
@@ -156,6 +155,7 @@ public function destroy(Request $request, int $id): JsonResponse|Response
|
|||||||
// HTMX 요청인 경우 갱신된 테이블 반환
|
// HTMX 요청인 경우 갱신된 테이블 반환
|
||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
$accounts = $this->bankAccountService->getAccounts($request->all(), $request->integer('per_page', 15));
|
$accounts = $this->bankAccountService->getAccounts($request->all(), $request->integer('per_page', 15));
|
||||||
|
|
||||||
return response(view('finance.accounts.partials.table', compact('accounts')));
|
return response(view('finance.accounts.partials.table', compact('accounts')));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +184,7 @@ public function restore(Request $request, int $id): JsonResponse|Response
|
|||||||
// HTMX 요청인 경우 갱신된 테이블 반환
|
// HTMX 요청인 경우 갱신된 테이블 반환
|
||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
$accounts = $this->bankAccountService->getAccounts($request->all(), $request->integer('per_page', 15));
|
$accounts = $this->bankAccountService->getAccounts($request->all(), $request->integer('per_page', 15));
|
||||||
|
|
||||||
return response(view('finance.accounts.partials.table', compact('accounts')));
|
return response(view('finance.accounts.partials.table', compact('accounts')));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +213,7 @@ public function forceDelete(Request $request, int $id): JsonResponse|Response
|
|||||||
// HTMX 요청인 경우 갱신된 테이블 반환
|
// HTMX 요청인 경우 갱신된 테이블 반환
|
||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
$accounts = $this->bankAccountService->getAccounts($request->all(), $request->integer('per_page', 15));
|
$accounts = $this->bankAccountService->getAccounts($request->all(), $request->integer('per_page', 15));
|
||||||
|
|
||||||
return response(view('finance.accounts.partials.table', compact('accounts')));
|
return response(view('finance.accounts.partials.table', compact('accounts')));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +242,7 @@ public function toggleActive(Request $request, int $id): JsonResponse|Response
|
|||||||
// HTMX 요청인 경우 갱신된 테이블 반환
|
// HTMX 요청인 경우 갱신된 테이블 반환
|
||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
$accounts = $this->bankAccountService->getAccounts($request->all(), $request->integer('per_page', 15));
|
$accounts = $this->bankAccountService->getAccounts($request->all(), $request->integer('per_page', 15));
|
||||||
|
|
||||||
return response(view('finance.accounts.partials.table', compact('accounts')));
|
return response(view('finance.accounts.partials.table', compact('accounts')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public function subscriptions(Request $request): JsonResponse|Response
|
|||||||
->orderBy('service_type');
|
->orderBy('service_type');
|
||||||
|
|
||||||
// 테넌트 필터링
|
// 테넌트 필터링
|
||||||
if (!$isHeadquarters && !$allTenants) {
|
if (! $isHeadquarters && ! $allTenants) {
|
||||||
$query->whereHas('member', function ($q) use ($tenantId) {
|
$query->whereHas('member', function ($q) use ($tenantId) {
|
||||||
$q->where('tenant_id', $tenantId);
|
$q->where('tenant_id', $tenantId);
|
||||||
});
|
});
|
||||||
@@ -102,7 +102,7 @@ public function cancelSubscription(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$result = $this->billingService->cancelSubscription($id);
|
$result = $this->billingService->cancelSubscription($id);
|
||||||
|
|
||||||
if (!$result) {
|
if (! $result) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '구독을 찾을 수 없습니다.',
|
'message' => '구독을 찾을 수 없습니다.',
|
||||||
@@ -121,7 +121,7 @@ public function cancelSubscription(int $id): JsonResponse
|
|||||||
public function memberSubscriptions(int $memberId): JsonResponse|Response
|
public function memberSubscriptions(int $memberId): JsonResponse|Response
|
||||||
{
|
{
|
||||||
$member = BarobillMember::with('tenant')->find($memberId);
|
$member = BarobillMember::with('tenant')->find($memberId);
|
||||||
if (!$member) {
|
if (! $member) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '회원사를 찾을 수 없습니다.',
|
'message' => '회원사를 찾을 수 없습니다.',
|
||||||
@@ -157,7 +157,7 @@ public function billingList(Request $request): JsonResponse|Response
|
|||||||
$billingMonth = $request->input('billing_month', now()->format('Y-m'));
|
$billingMonth = $request->input('billing_month', now()->format('Y-m'));
|
||||||
|
|
||||||
// 테넌트 필터링
|
// 테넌트 필터링
|
||||||
$filterTenantId = (!$isHeadquarters && !$allTenants) ? $tenantId : null;
|
$filterTenantId = (! $isHeadquarters && ! $allTenants) ? $tenantId : null;
|
||||||
|
|
||||||
$summaries = BarobillMonthlySummary::with(['member.tenant'])
|
$summaries = BarobillMonthlySummary::with(['member.tenant'])
|
||||||
->where('billing_month', $billingMonth)
|
->where('billing_month', $billingMonth)
|
||||||
@@ -202,7 +202,7 @@ public function billingStats(Request $request): JsonResponse|Response
|
|||||||
|
|
||||||
$billingMonth = $request->input('billing_month', now()->format('Y-m'));
|
$billingMonth = $request->input('billing_month', now()->format('Y-m'));
|
||||||
|
|
||||||
$filterTenantId = (!$isHeadquarters && !$allTenants) ? $tenantId : null;
|
$filterTenantId = (! $isHeadquarters && ! $allTenants) ? $tenantId : null;
|
||||||
$stats = $this->billingService->getMonthlyTotal($billingMonth, $filterTenantId);
|
$stats = $this->billingService->getMonthlyTotal($billingMonth, $filterTenantId);
|
||||||
|
|
||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
@@ -225,7 +225,7 @@ public function billingStats(Request $request): JsonResponse|Response
|
|||||||
public function memberBilling(Request $request, int $memberId): JsonResponse|Response
|
public function memberBilling(Request $request, int $memberId): JsonResponse|Response
|
||||||
{
|
{
|
||||||
$member = BarobillMember::with('tenant')->find($memberId);
|
$member = BarobillMember::with('tenant')->find($memberId);
|
||||||
if (!$member) {
|
if (! $member) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '회원사를 찾을 수 없습니다.',
|
'message' => '회원사를 찾을 수 없습니다.',
|
||||||
@@ -293,7 +293,7 @@ public function yearlyTrend(Request $request): JsonResponse
|
|||||||
|
|
||||||
$year = $request->input('year', now()->year);
|
$year = $request->input('year', now()->year);
|
||||||
|
|
||||||
$filterTenantId = (!$isHeadquarters && !$allTenants) ? $tenantId : null;
|
$filterTenantId = (! $isHeadquarters && ! $allTenants) ? $tenantId : null;
|
||||||
$trend = $this->billingService->getYearlyTrend($year, $filterTenantId);
|
$trend = $this->billingService->getYearlyTrend($year, $filterTenantId);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@@ -313,7 +313,7 @@ public function export(Request $request)
|
|||||||
|
|
||||||
$billingMonth = $request->input('billing_month', now()->format('Y-m'));
|
$billingMonth = $request->input('billing_month', now()->format('Y-m'));
|
||||||
|
|
||||||
$filterTenantId = (!$isHeadquarters && !$allTenants) ? $tenantId : null;
|
$filterTenantId = (! $isHeadquarters && ! $allTenants) ? $tenantId : null;
|
||||||
|
|
||||||
$summaries = BarobillMonthlySummary::with(['member.tenant'])
|
$summaries = BarobillMonthlySummary::with(['member.tenant'])
|
||||||
->where('billing_month', $billingMonth)
|
->where('billing_month', $billingMonth)
|
||||||
@@ -336,7 +336,7 @@ public function export(Request $request)
|
|||||||
|
|
||||||
$callback = function () use ($summaries, $total, $isHeadquarters, $allTenants) {
|
$callback = function () use ($summaries, $total, $isHeadquarters, $allTenants) {
|
||||||
$file = fopen('php://output', 'w');
|
$file = fopen('php://output', 'w');
|
||||||
fprintf($file, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF));
|
||||||
|
|
||||||
// 헤더
|
// 헤더
|
||||||
$headerRow = ['사업자번호', '상호', '계좌조회', '카드내역', '홈텍스', '월정액합계', '세금계산서(건)', '세금계산서(원)', '건별합계', '총합계'];
|
$headerRow = ['사업자번호', '상호', '계좌조회', '카드내역', '홈텍스', '월정액합계', '세금계산서(건)', '세금계산서(원)', '건별합계', '총합계'];
|
||||||
@@ -411,7 +411,7 @@ public function pricingPolicies(Request $request): JsonResponse|Response
|
|||||||
public function updatePricingPolicy(Request $request, int $id): JsonResponse
|
public function updatePricingPolicy(Request $request, int $id): JsonResponse
|
||||||
{
|
{
|
||||||
$policy = BarobillPricingPolicy::find($id);
|
$policy = BarobillPricingPolicy::find($id);
|
||||||
if (!$policy) {
|
if (! $policy) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '정책을 찾을 수 없습니다.',
|
'message' => '정책을 찾을 수 없습니다.',
|
||||||
@@ -444,7 +444,7 @@ public function updatePricingPolicy(Request $request, int $id): JsonResponse
|
|||||||
public function getPricingPolicy(int $id): JsonResponse
|
public function getPricingPolicy(int $id): JsonResponse
|
||||||
{
|
{
|
||||||
$policy = BarobillPricingPolicy::find($id);
|
$policy = BarobillPricingPolicy::find($id);
|
||||||
if (!$policy) {
|
if (! $policy) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '정책을 찾을 수 없습니다.',
|
'message' => '정책을 찾을 수 없습니다.',
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public function show(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$config = BarobillConfig::find($id);
|
$config = BarobillConfig::find($id);
|
||||||
|
|
||||||
if (!$config) {
|
if (! $config) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '설정을 찾을 수 없습니다.',
|
'message' => '설정을 찾을 수 없습니다.',
|
||||||
@@ -114,7 +114,7 @@ public function update(Request $request, int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$config = BarobillConfig::find($id);
|
$config = BarobillConfig::find($id);
|
||||||
|
|
||||||
if (!$config) {
|
if (! $config) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '설정을 찾을 수 없습니다.',
|
'message' => '설정을 찾을 수 없습니다.',
|
||||||
@@ -167,7 +167,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$config = BarobillConfig::find($id);
|
$config = BarobillConfig::find($id);
|
||||||
|
|
||||||
if (!$config) {
|
if (! $config) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '설정을 찾을 수 없습니다.',
|
'message' => '설정을 찾을 수 없습니다.',
|
||||||
@@ -196,7 +196,7 @@ public function toggleActive(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$config = BarobillConfig::find($id);
|
$config = BarobillConfig::find($id);
|
||||||
|
|
||||||
if (!$config) {
|
if (! $config) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '설정을 찾을 수 없습니다.',
|
'message' => '설정을 찾을 수 없습니다.',
|
||||||
@@ -205,14 +205,14 @@ public function toggleActive(int $id): JsonResponse
|
|||||||
|
|
||||||
DB::beginTransaction();
|
DB::beginTransaction();
|
||||||
try {
|
try {
|
||||||
if (!$config->is_active) {
|
if (! $config->is_active) {
|
||||||
// 활성화하려면 같은 환경의 다른 설정들 비활성화
|
// 활성화하려면 같은 환경의 다른 설정들 비활성화
|
||||||
BarobillConfig::where('environment', $config->environment)
|
BarobillConfig::where('environment', $config->environment)
|
||||||
->where('id', '!=', $id)
|
->where('id', '!=', $id)
|
||||||
->update(['is_active' => false]);
|
->update(['is_active' => false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$config->update(['is_active' => !$config->is_active]);
|
$config->update(['is_active' => ! $config->is_active]);
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
|
||||||
@@ -231,5 +231,4 @@ public function toggleActive(int $id): JsonResponse
|
|||||||
], 500);
|
], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public function show(): JsonResponse
|
|||||||
// 바로빌 회원사 정보 조회 (담당자 정보 기본값용)
|
// 바로빌 회원사 정보 조회 (담당자 정보 기본값용)
|
||||||
$barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
|
$barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
|
||||||
|
|
||||||
if (!$setting) {
|
if (! $setting) {
|
||||||
// 설정이 없으면 바로빌 회원사 정보를 기본값으로 사용
|
// 설정이 없으면 바로빌 회원사 정보를 기본값으로 사용
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
@@ -158,7 +158,7 @@ public function checkService(string $service): JsonResponse
|
|||||||
|
|
||||||
$setting = BarobillSetting::where('tenant_id', $tenantId)->first();
|
$setting = BarobillSetting::where('tenant_id', $tenantId)->first();
|
||||||
|
|
||||||
if (!$setting) {
|
if (! $setting) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'enabled' => false,
|
'enabled' => false,
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 바로빌 사용량조회 API 컨트롤러
|
* 바로빌 사용량조회 API 컨트롤러
|
||||||
@@ -44,7 +43,7 @@ public function index(Request $request): JsonResponse|Response
|
|||||||
$apiEndDate = str_replace('-', '', $endDate);
|
$apiEndDate = str_replace('-', '', $endDate);
|
||||||
|
|
||||||
// 테넌트 필터링
|
// 테넌트 필터링
|
||||||
$filterTenantId = (!$isHeadquarters && !$allTenants && $tenantId) ? $tenantId : null;
|
$filterTenantId = (! $isHeadquarters && ! $allTenants && $tenantId) ? $tenantId : null;
|
||||||
|
|
||||||
// 사용량 목록 조회
|
// 사용량 목록 조회
|
||||||
$usageList = $this->usageService->getUsageList($apiStartDate, $apiEndDate, $filterTenantId);
|
$usageList = $this->usageService->getUsageList($apiStartDate, $apiEndDate, $filterTenantId);
|
||||||
@@ -117,7 +116,7 @@ public function stats(Request $request): JsonResponse|Response
|
|||||||
$apiEndDate = str_replace('-', '', $endDate);
|
$apiEndDate = str_replace('-', '', $endDate);
|
||||||
|
|
||||||
// 테넌트 필터링
|
// 테넌트 필터링
|
||||||
$filterTenantId = (!$isHeadquarters && !$allTenants && $tenantId) ? $tenantId : null;
|
$filterTenantId = (! $isHeadquarters && ! $allTenants && $tenantId) ? $tenantId : null;
|
||||||
|
|
||||||
// 사용량 목록 조회 및 통계 집계
|
// 사용량 목록 조회 및 통계 집계
|
||||||
$usageList = $this->usageService->getUsageList($apiStartDate, $apiEndDate, $filterTenantId);
|
$usageList = $this->usageService->getUsageList($apiStartDate, $apiEndDate, $filterTenantId);
|
||||||
@@ -145,7 +144,7 @@ public function show(Request $request, int $memberId): JsonResponse|Response
|
|||||||
{
|
{
|
||||||
$member = BarobillMember::with('tenant:id,company_name')->find($memberId);
|
$member = BarobillMember::with('tenant:id,company_name')->find($memberId);
|
||||||
|
|
||||||
if (!$member) {
|
if (! $member) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '회원사를 찾을 수 없습니다.',
|
'message' => '회원사를 찾을 수 없습니다.',
|
||||||
@@ -209,7 +208,7 @@ public function export(Request $request)
|
|||||||
$apiEndDate = str_replace('-', '', $endDate);
|
$apiEndDate = str_replace('-', '', $endDate);
|
||||||
|
|
||||||
// 테넌트 필터링
|
// 테넌트 필터링
|
||||||
$filterTenantId = (!$isHeadquarters && !$allTenants && $tenantId) ? $tenantId : null;
|
$filterTenantId = (! $isHeadquarters && ! $allTenants && $tenantId) ? $tenantId : null;
|
||||||
|
|
||||||
// 사용량 목록 조회
|
// 사용량 목록 조회
|
||||||
$usageList = $this->usageService->getUsageList($apiStartDate, $apiEndDate, $filterTenantId);
|
$usageList = $this->usageService->getUsageList($apiStartDate, $apiEndDate, $filterTenantId);
|
||||||
@@ -227,7 +226,7 @@ public function export(Request $request)
|
|||||||
$file = fopen('php://output', 'w');
|
$file = fopen('php://output', 'w');
|
||||||
|
|
||||||
// BOM for Excel UTF-8
|
// BOM for Excel UTF-8
|
||||||
fprintf($file, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF));
|
||||||
|
|
||||||
// 헤더
|
// 헤더
|
||||||
$headerRow = ['사업자번호', '상호', '바로빌ID', '세금계산서(건)', '계좌조회(건)', '카드내역(건)', '홈텍스(건)', '과금액(원)'];
|
$headerRow = ['사업자번호', '상호', '바로빌ID', '세금계산서(건)', '계좌조회(건)', '카드내역(건)', '홈텍스(건)', '과금액(원)'];
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
*/
|
*/
|
||||||
public function forceDestroy(int $id): JsonResponse
|
public function forceDestroy(int $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!auth()->user()?->is_super_admin) {
|
if (! auth()->user()?->is_super_admin) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '슈퍼관리자만 영구 삭제할 수 있습니다.',
|
'message' => '슈퍼관리자만 영구 삭제할 수 있습니다.',
|
||||||
@@ -263,7 +263,7 @@ public function forceDestroy(int $id): JsonResponse
|
|||||||
*/
|
*/
|
||||||
public function restore(int $id): JsonResponse
|
public function restore(int $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!auth()->user()?->is_super_admin) {
|
if (! auth()->user()?->is_super_admin) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '슈퍼관리자만 복원할 수 있습니다.',
|
'message' => '슈퍼관리자만 복원할 수 있습니다.',
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public function index(Request $request): View
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 활성 상태 필터
|
// 활성 상태 필터
|
||||||
if ($request->filled('is_active') && !$showTrashed) {
|
if ($request->filled('is_active') && ! $showTrashed) {
|
||||||
$query->where('is_active', $request->boolean('is_active'));
|
$query->where('is_active', $request->boolean('is_active'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +290,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
*/
|
*/
|
||||||
public function forceDestroy(int $id): JsonResponse
|
public function forceDestroy(int $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!auth()->user()?->is_super_admin) {
|
if (! auth()->user()?->is_super_admin) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '슈퍼관리자만 영구 삭제할 수 있습니다.',
|
'message' => '슈퍼관리자만 영구 삭제할 수 있습니다.',
|
||||||
@@ -337,7 +337,7 @@ public function forceDestroy(int $id): JsonResponse
|
|||||||
*/
|
*/
|
||||||
public function restore(int $id): JsonResponse
|
public function restore(int $id): JsonResponse
|
||||||
{
|
{
|
||||||
if (!auth()->user()?->is_super_admin) {
|
if (! auth()->user()?->is_super_admin) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '슈퍼관리자만 복원할 수 있습니다.',
|
'message' => '슈퍼관리자만 복원할 수 있습니다.',
|
||||||
@@ -535,7 +535,7 @@ public function uploadImage(Request $request): JsonResponse
|
|||||||
}
|
}
|
||||||
|
|
||||||
// API 토큰 교환
|
// API 토큰 교환
|
||||||
$tokenService = new \App\Services\ApiTokenService();
|
$tokenService = new \App\Services\ApiTokenService;
|
||||||
$userId = auth()->id();
|
$userId = auth()->id();
|
||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ public function destroy(Request $request, int $id): JsonResponse|Response
|
|||||||
$month = $request->integer('month', now()->month);
|
$month = $request->integer('month', now()->month);
|
||||||
$calendarData = $this->fundScheduleService->getCalendarData($year, $month);
|
$calendarData = $this->fundScheduleService->getCalendarData($year, $month);
|
||||||
$summary = $this->fundScheduleService->getMonthlySummary($year, $month);
|
$summary = $this->fundScheduleService->getMonthlySummary($year, $month);
|
||||||
|
|
||||||
return response(view('finance.fund-schedules.partials.calendar', compact('year', 'month', 'calendarData', 'summary')));
|
return response(view('finance.fund-schedules.partials.calendar', compact('year', 'month', 'calendarData', 'summary')));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +230,7 @@ public function updateStatus(Request $request, int $id): JsonResponse|Response
|
|||||||
$month = $request->integer('month', now()->month);
|
$month = $request->integer('month', now()->month);
|
||||||
$calendarData = $this->fundScheduleService->getCalendarData($year, $month);
|
$calendarData = $this->fundScheduleService->getCalendarData($year, $month);
|
||||||
$summary = $this->fundScheduleService->getMonthlySummary($year, $month);
|
$summary = $this->fundScheduleService->getMonthlySummary($year, $month);
|
||||||
|
|
||||||
return response(view('finance.fund-schedules.partials.calendar', compact('year', 'month', 'calendarData', 'summary')));
|
return response(view('finance.fund-schedules.partials.calendar', compact('year', 'month', 'calendarData', 'summary')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ public function bulkCopyToTenant(Request $request): JsonResponse
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skipped++;
|
$skipped++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,6 +289,7 @@ public function bulkCopyToTenant(Request $request): JsonResponse
|
|||||||
]);
|
]);
|
||||||
$idMap[$gc->id] = $trashed->id;
|
$idMap[$gc->id] = $trashed->id;
|
||||||
$copied++;
|
$copied++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public function search(Request $request): JsonResponse
|
|||||||
->where('user_tenants.tenant_id', $tenantId)
|
->where('user_tenants.tenant_id', $tenantId)
|
||||||
->where('user_tenants.is_active', true);
|
->where('user_tenants.is_active', true);
|
||||||
})
|
})
|
||||||
->leftJoin('departments', function ($join) use ($tenantId) {
|
->leftJoin('departments', function ($join) {
|
||||||
$join->on('departments.id', '=', DB::raw('(
|
$join->on('departments.id', '=', DB::raw('(
|
||||||
SELECT du.department_id FROM department_user du
|
SELECT du.department_id FROM department_user du
|
||||||
WHERE du.user_id = users.id AND du.is_primary = 1
|
WHERE du.user_id = users.id AND du.is_primary = 1
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
use App\Services\AppVersionService;
|
use App\Services\AppVersionService;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\View\View;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\View\View;
|
||||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
|
||||||
class AppVersionController extends Controller
|
class AppVersionController extends Controller
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ public function settings(Request $request): View|Response
|
|||||||
$hasBarobillLogin = false;
|
$hasBarobillLogin = false;
|
||||||
if ($barobillMember) {
|
if ($barobillMember) {
|
||||||
try {
|
try {
|
||||||
$hasBarobillLogin = !empty($barobillMember->barobill_id) && !empty($barobillMember->barobill_pwd);
|
$hasBarobillLogin = ! empty($barobillMember->barobill_id) && ! empty($barobillMember->barobill_pwd);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$hasBarobillLogin = !empty($barobillMember->barobill_id) && !empty($barobillMember->getRawOriginal('barobill_pwd'));
|
$hasBarobillLogin = ! empty($barobillMember->barobill_id) && ! empty($barobillMember->getRawOriginal('barobill_pwd'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,13 @@ class EtaxController extends Controller
|
|||||||
* 바로빌 SOAP 설정
|
* 바로빌 SOAP 설정
|
||||||
*/
|
*/
|
||||||
private ?string $certKey = null;
|
private ?string $certKey = null;
|
||||||
|
|
||||||
private ?string $corpNum = null;
|
private ?string $corpNum = null;
|
||||||
|
|
||||||
private bool $isTestMode = false;
|
private bool $isTestMode = false;
|
||||||
|
|
||||||
private ?string $soapUrl = null;
|
private ?string $soapUrl = null;
|
||||||
|
|
||||||
private ?\SoapClient $soapClient = null;
|
private ?\SoapClient $soapClient = null;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
@@ -35,7 +39,7 @@ public function __construct()
|
|||||||
$this->certKey = $activeConfig->cert_key;
|
$this->certKey = $activeConfig->cert_key;
|
||||||
$this->corpNum = $activeConfig->corp_num;
|
$this->corpNum = $activeConfig->corp_num;
|
||||||
$this->isTestMode = $activeConfig->environment === 'test';
|
$this->isTestMode = $activeConfig->environment === 'test';
|
||||||
$this->soapUrl = $activeConfig->base_url . '/TI.asmx?WSDL';
|
$this->soapUrl = $activeConfig->base_url.'/TI.asmx?WSDL';
|
||||||
} else {
|
} else {
|
||||||
// 설정이 없으면 기본값 사용
|
// 설정이 없으면 기본값 사용
|
||||||
$this->isTestMode = config('services.barobill.test_mode', true);
|
$this->isTestMode = config('services.barobill.test_mode', true);
|
||||||
@@ -57,14 +61,14 @@ public function __construct()
|
|||||||
*/
|
*/
|
||||||
private function initSoapClient(): void
|
private function initSoapClient(): void
|
||||||
{
|
{
|
||||||
if (!empty($this->certKey) || $this->isTestMode) {
|
if (! empty($this->certKey) || $this->isTestMode) {
|
||||||
try {
|
try {
|
||||||
$context = stream_context_create([
|
$context = stream_context_create([
|
||||||
'ssl' => [
|
'ssl' => [
|
||||||
'verify_peer' => false,
|
'verify_peer' => false,
|
||||||
'verify_peer_name' => false,
|
'verify_peer_name' => false,
|
||||||
'allow_self_signed' => true
|
'allow_self_signed' => true,
|
||||||
]
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->soapClient = new \SoapClient($this->soapUrl, [
|
$this->soapClient = new \SoapClient($this->soapUrl, [
|
||||||
@@ -73,10 +77,10 @@ private function initSoapClient(): void
|
|||||||
'exceptions' => true,
|
'exceptions' => true,
|
||||||
'connection_timeout' => 30,
|
'connection_timeout' => 30,
|
||||||
'stream_context' => $context,
|
'stream_context' => $context,
|
||||||
'cache_wsdl' => WSDL_CACHE_NONE
|
'cache_wsdl' => WSDL_CACHE_NONE,
|
||||||
]);
|
]);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
Log::error('바로빌 SOAP 클라이언트 생성 실패: ' . $e->getMessage());
|
Log::error('바로빌 SOAP 클라이언트 생성 실패: '.$e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,14 +139,14 @@ private function applyMemberServerMode(BarobillMember $member): void
|
|||||||
$baseUrl = $config->base_url ?: ($memberTestMode
|
$baseUrl = $config->base_url ?: ($memberTestMode
|
||||||
? 'https://testws.baroservice.com'
|
? 'https://testws.baroservice.com'
|
||||||
: 'https://ws.baroservice.com');
|
: 'https://ws.baroservice.com');
|
||||||
$this->soapUrl = $baseUrl . '/TI.asmx?WSDL';
|
$this->soapUrl = $baseUrl.'/TI.asmx?WSDL';
|
||||||
|
|
||||||
// SOAP 클라이언트 재초기화
|
// SOAP 클라이언트 재초기화
|
||||||
$this->initSoapClient();
|
$this->initSoapClient();
|
||||||
|
|
||||||
Log::info('[Etax] 서버 모드 적용', [
|
Log::info('[Etax] 서버 모드 적용', [
|
||||||
'targetEnv' => $targetEnv,
|
'targetEnv' => $targetEnv,
|
||||||
'certKey' => substr($this->certKey ?? '', 0, 10) . '...',
|
'certKey' => substr($this->certKey ?? '', 0, 10).'...',
|
||||||
'corpNum' => $this->corpNum,
|
'corpNum' => $this->corpNum,
|
||||||
'soapUrl' => $this->soapUrl,
|
'soapUrl' => $this->soapUrl,
|
||||||
]);
|
]);
|
||||||
@@ -173,7 +177,7 @@ public function getInvoices(): JsonResponse
|
|||||||
$allInvoices = $data['invoices'] ?? [];
|
$allInvoices = $data['invoices'] ?? [];
|
||||||
|
|
||||||
// 본사(테넌트 1)가 아니면 해당 테넌트의 세금계산서만 필터링
|
// 본사(테넌트 1)가 아니면 해당 테넌트의 세금계산서만 필터링
|
||||||
if (!$isHeadquarters && $tenantId) {
|
if (! $isHeadquarters && $tenantId) {
|
||||||
$invoices = array_values(array_filter($allInvoices, function ($invoice) use ($tenantId) {
|
$invoices = array_values(array_filter($allInvoices, function ($invoice) use ($tenantId) {
|
||||||
return ($invoice['tenant_id'] ?? null) == $tenantId;
|
return ($invoice['tenant_id'] ?? null) == $tenantId;
|
||||||
}));
|
}));
|
||||||
@@ -204,12 +208,12 @@ public function issue(Request $request): JsonResponse
|
|||||||
|
|
||||||
$input = $request->all();
|
$input = $request->all();
|
||||||
|
|
||||||
$useRealAPI = $this->soapClient !== null && ($this->isTestMode || !empty($this->certKey));
|
$useRealAPI = $this->soapClient !== null && ($this->isTestMode || ! empty($this->certKey));
|
||||||
|
|
||||||
$debugInfo = [
|
$debugInfo = [
|
||||||
'hasSoapClient' => $this->soapClient !== null,
|
'hasSoapClient' => $this->soapClient !== null,
|
||||||
'hasCertKey' => !empty($this->certKey),
|
'hasCertKey' => ! empty($this->certKey),
|
||||||
'hasCorpNum' => !empty($this->corpNum),
|
'hasCorpNum' => ! empty($this->corpNum),
|
||||||
'isTestMode' => $this->isTestMode,
|
'isTestMode' => $this->isTestMode,
|
||||||
'willUseRealAPI' => $useRealAPI,
|
'willUseRealAPI' => $useRealAPI,
|
||||||
];
|
];
|
||||||
@@ -218,7 +222,7 @@ public function issue(Request $request): JsonResponse
|
|||||||
$apiResult = $this->issueTaxInvoice($input);
|
$apiResult = $this->issueTaxInvoice($input);
|
||||||
|
|
||||||
if ($apiResult['success']) {
|
if ($apiResult['success']) {
|
||||||
$mgtKey = $input['issueKey'] ?? 'MGT' . date('YmdHis') . rand(1000, 9999);
|
$mgtKey = $input['issueKey'] ?? 'MGT'.date('YmdHis').rand(1000, 9999);
|
||||||
|
|
||||||
$newInvoice = $this->createInvoiceRecord($input, $mgtKey, $apiResult['data'] ?? null);
|
$newInvoice = $this->createInvoiceRecord($input, $mgtKey, $apiResult['data'] ?? null);
|
||||||
$this->saveInvoice($newInvoice);
|
$this->saveInvoice($newInvoice);
|
||||||
@@ -245,7 +249,7 @@ public function issue(Request $request): JsonResponse
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 시뮬레이션 모드
|
// 시뮬레이션 모드
|
||||||
$issueKey = 'BARO-' . date('Y') . '-' . str_pad(rand(1, 9999), 4, '0', STR_PAD_LEFT);
|
$issueKey = 'BARO-'.date('Y').'-'.str_pad(rand(1, 9999), 4, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
$newInvoice = $this->createInvoiceRecord($input, $issueKey, null);
|
$newInvoice = $this->createInvoiceRecord($input, $issueKey, null);
|
||||||
$this->saveInvoice($newInvoice);
|
$this->saveInvoice($newInvoice);
|
||||||
@@ -293,16 +297,16 @@ public function sendToNts(Request $request): JsonResponse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$invoice) {
|
if (! $invoice) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => '세금계산서를 찾을 수 없습니다.',
|
'error' => '세금계산서를 찾을 수 없습니다.',
|
||||||
], 404);
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$useRealAPI = $this->soapClient !== null && !empty($this->certKey);
|
$useRealAPI = $this->soapClient !== null && ! empty($this->certKey);
|
||||||
|
|
||||||
if ($useRealAPI && !empty($invoice['mgtKey'])) {
|
if ($useRealAPI && ! empty($invoice['mgtKey'])) {
|
||||||
$result = $this->callBarobillSOAP('SendToNTS', [
|
$result = $this->callBarobillSOAP('SendToNTS', [
|
||||||
'CorpNum' => $this->corpNum,
|
'CorpNum' => $this->corpNum,
|
||||||
'MgtKey' => $invoice['mgtKey'],
|
'MgtKey' => $invoice['mgtKey'],
|
||||||
@@ -310,7 +314,7 @@ public function sendToNts(Request $request): JsonResponse
|
|||||||
|
|
||||||
if ($result['success']) {
|
if ($result['success']) {
|
||||||
$data['invoices'][$invoiceIndex]['status'] = 'sent';
|
$data['invoices'][$invoiceIndex]['status'] = 'sent';
|
||||||
$data['invoices'][$invoiceIndex]['ntsReceiptNo'] = 'NTS-' . date('YmdHis');
|
$data['invoices'][$invoiceIndex]['ntsReceiptNo'] = 'NTS-'.date('YmdHis');
|
||||||
$data['invoices'][$invoiceIndex]['sentAt'] = date('Y-m-d');
|
$data['invoices'][$invoiceIndex]['sentAt'] = date('Y-m-d');
|
||||||
file_put_contents($dataFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
file_put_contents($dataFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||||
|
|
||||||
@@ -327,7 +331,7 @@ public function sendToNts(Request $request): JsonResponse
|
|||||||
} else {
|
} else {
|
||||||
// 시뮬레이션
|
// 시뮬레이션
|
||||||
$data['invoices'][$invoiceIndex]['status'] = 'sent';
|
$data['invoices'][$invoiceIndex]['status'] = 'sent';
|
||||||
$data['invoices'][$invoiceIndex]['ntsReceiptNo'] = 'NTS-SIM-' . date('YmdHis');
|
$data['invoices'][$invoiceIndex]['ntsReceiptNo'] = 'NTS-SIM-'.date('YmdHis');
|
||||||
$data['invoices'][$invoiceIndex]['sentAt'] = date('Y-m-d');
|
$data['invoices'][$invoiceIndex]['sentAt'] = date('Y-m-d');
|
||||||
file_put_contents($dataFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
file_put_contents($dataFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||||
|
|
||||||
@@ -345,12 +349,12 @@ public function sendToNts(Request $request): JsonResponse
|
|||||||
public function getSupplier(): JsonResponse
|
public function getSupplier(): JsonResponse
|
||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id');
|
$tenantId = session('selected_tenant_id');
|
||||||
if (!$tenantId) {
|
if (! $tenantId) {
|
||||||
return response()->json(['success' => false, 'error' => '테넌트가 선택되지 않았습니다.'], 400);
|
return response()->json(['success' => false, 'error' => '테넌트가 선택되지 않았습니다.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
$member = BarobillMember::where('tenant_id', $tenantId)->first();
|
$member = BarobillMember::where('tenant_id', $tenantId)->first();
|
||||||
if (!$member) {
|
if (! $member) {
|
||||||
return response()->json(['success' => false, 'error' => '바로빌 회원사 정보가 없습니다.'], 404);
|
return response()->json(['success' => false, 'error' => '바로빌 회원사 정보가 없습니다.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,12 +380,12 @@ public function getSupplier(): JsonResponse
|
|||||||
public function updateSupplier(Request $request): JsonResponse
|
public function updateSupplier(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id');
|
$tenantId = session('selected_tenant_id');
|
||||||
if (!$tenantId) {
|
if (! $tenantId) {
|
||||||
return response()->json(['success' => false, 'error' => '테넌트가 선택되지 않았습니다.'], 400);
|
return response()->json(['success' => false, 'error' => '테넌트가 선택되지 않았습니다.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
$member = BarobillMember::where('tenant_id', $tenantId)->first();
|
$member = BarobillMember::where('tenant_id', $tenantId)->first();
|
||||||
if (!$member) {
|
if (! $member) {
|
||||||
return response()->json(['success' => false, 'error' => '바로빌 회원사 정보가 없습니다.'], 404);
|
return response()->json(['success' => false, 'error' => '바로빌 회원사 정보가 없습니다.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,7 +428,7 @@ public function delete(Request $request): JsonResponse
|
|||||||
|
|
||||||
$dataFile = storage_path('app/barobill/invoices_data.json');
|
$dataFile = storage_path('app/barobill/invoices_data.json');
|
||||||
|
|
||||||
if (!file_exists($dataFile)) {
|
if (! file_exists($dataFile)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => '데이터 파일이 없습니다.',
|
'error' => '데이터 파일이 없습니다.',
|
||||||
@@ -434,7 +438,7 @@ public function delete(Request $request): JsonResponse
|
|||||||
$data = json_decode(file_get_contents($dataFile), true) ?? ['invoices' => []];
|
$data = json_decode(file_get_contents($dataFile), true) ?? ['invoices' => []];
|
||||||
|
|
||||||
$originalCount = count($data['invoices']);
|
$originalCount = count($data['invoices']);
|
||||||
$data['invoices'] = array_values(array_filter($data['invoices'], fn($inv) => $inv['id'] !== $invoiceId));
|
$data['invoices'] = array_values(array_filter($data['invoices'], fn ($inv) => $inv['id'] !== $invoiceId));
|
||||||
|
|
||||||
if (count($data['invoices']) === $originalCount) {
|
if (count($data['invoices']) === $originalCount) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@@ -456,20 +460,20 @@ public function delete(Request $request): JsonResponse
|
|||||||
*/
|
*/
|
||||||
private function callBarobillSOAP(string $method, array $params = []): array
|
private function callBarobillSOAP(string $method, array $params = []): array
|
||||||
{
|
{
|
||||||
if (!$this->soapClient) {
|
if (! $this->soapClient) {
|
||||||
return [
|
return [
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => '바로빌 SOAP 클라이언트가 초기화되지 않았습니다.',
|
'error' => '바로빌 SOAP 클라이언트가 초기화되지 않았습니다.',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($params['CERTKEY'])) {
|
if (! isset($params['CERTKEY'])) {
|
||||||
$params['CERTKEY'] = $this->certKey;
|
$params['CERTKEY'] = $this->certKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$result = $this->soapClient->$method($params);
|
$result = $this->soapClient->$method($params);
|
||||||
$resultProperty = $method . 'Result';
|
$resultProperty = $method.'Result';
|
||||||
|
|
||||||
if (isset($result->$resultProperty)) {
|
if (isset($result->$resultProperty)) {
|
||||||
$resultData = $result->$resultProperty;
|
$resultData = $result->$resultProperty;
|
||||||
@@ -477,7 +481,7 @@ private function callBarobillSOAP(string $method, array $params = []): array
|
|||||||
if (is_numeric($resultData) && $resultData < 0) {
|
if (is_numeric($resultData) && $resultData < 0) {
|
||||||
return [
|
return [
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => '바로빌 API 오류 코드: ' . $resultData,
|
'error' => '바로빌 API 오류 코드: '.$resultData,
|
||||||
'error_code' => $resultData,
|
'error_code' => $resultData,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -495,12 +499,12 @@ private function callBarobillSOAP(string $method, array $params = []): array
|
|||||||
} catch (\SoapFault $e) {
|
} catch (\SoapFault $e) {
|
||||||
return [
|
return [
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'SOAP 오류: ' . $e->getMessage(),
|
'error' => 'SOAP 오류: '.$e->getMessage(),
|
||||||
];
|
];
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return [
|
return [
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => 'API 호출 오류: ' . $e->getMessage(),
|
'error' => 'API 호출 오류: '.$e->getMessage(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -510,7 +514,7 @@ private function callBarobillSOAP(string $method, array $params = []): array
|
|||||||
*/
|
*/
|
||||||
private function issueTaxInvoice(array $invoiceData): array
|
private function issueTaxInvoice(array $invoiceData): array
|
||||||
{
|
{
|
||||||
$mgtKey = $invoiceData['issueKey'] ?? 'MGT' . date('YmdHis') . rand(1000, 9999);
|
$mgtKey = $invoiceData['issueKey'] ?? 'MGT'.date('YmdHis').rand(1000, 9999);
|
||||||
|
|
||||||
$supplyAmt = 0;
|
$supplyAmt = 0;
|
||||||
$vat = 0;
|
$vat = 0;
|
||||||
@@ -583,7 +587,7 @@ private function issueTaxInvoice(array $invoiceData): array
|
|||||||
$month = str_pad($item['month'] ?? '', 2, '0', STR_PAD_LEFT);
|
$month = str_pad($item['month'] ?? '', 2, '0', STR_PAD_LEFT);
|
||||||
$day = str_pad($item['day'] ?? '', 2, '0', STR_PAD_LEFT);
|
$day = str_pad($item['day'] ?? '', 2, '0', STR_PAD_LEFT);
|
||||||
$purchaseExpiry = ($month && $day && $month !== '00' && $day !== '00')
|
$purchaseExpiry = ($month && $day && $month !== '00' && $day !== '00')
|
||||||
? $year . $month . $day
|
? $year.$month.$day
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
$taxInvoice['TaxInvoiceTradeLineItems']['TaxInvoiceTradeLineItem'][] = [
|
$taxInvoice['TaxInvoiceTradeLineItems']['TaxInvoiceTradeLineItem'][] = [
|
||||||
@@ -613,7 +617,7 @@ private function issueTaxInvoice(array $invoiceData): array
|
|||||||
private function createInvoiceRecord(array $input, string $issueKey, $apiData): array
|
private function createInvoiceRecord(array $input, string $issueKey, $apiData): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => 'inv_' . time() . '_' . rand(1000, 9999),
|
'id' => 'inv_'.time().'_'.rand(1000, 9999),
|
||||||
'tenant_id' => session('selected_tenant_id'), // 테넌트별 필터링용
|
'tenant_id' => session('selected_tenant_id'), // 테넌트별 필터링용
|
||||||
'issueKey' => $issueKey,
|
'issueKey' => $issueKey,
|
||||||
'mgtKey' => $issueKey,
|
'mgtKey' => $issueKey,
|
||||||
@@ -638,7 +642,7 @@ private function createInvoiceRecord(array $input, string $issueKey, $apiData):
|
|||||||
'memo' => $input['memo'] ?? '',
|
'memo' => $input['memo'] ?? '',
|
||||||
'createdAt' => date('Y-m-d\TH:i:s'),
|
'createdAt' => date('Y-m-d\TH:i:s'),
|
||||||
'sentAt' => date('Y-m-d'),
|
'sentAt' => date('Y-m-d'),
|
||||||
'barobillInvoiceId' => is_numeric($apiData) ? (string)$apiData : '',
|
'barobillInvoiceId' => is_numeric($apiData) ? (string) $apiData : '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,11 +652,11 @@ private function createInvoiceRecord(array $input, string $issueKey, $apiData):
|
|||||||
private function saveInvoice(array $invoice): bool
|
private function saveInvoice(array $invoice): bool
|
||||||
{
|
{
|
||||||
$dataDir = storage_path('app/barobill');
|
$dataDir = storage_path('app/barobill');
|
||||||
if (!is_dir($dataDir)) {
|
if (! is_dir($dataDir)) {
|
||||||
mkdir($dataDir, 0755, true);
|
mkdir($dataDir, 0755, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dataFile = $dataDir . '/invoices_data.json';
|
$dataFile = $dataDir.'/invoices_data.json';
|
||||||
$existingData = ['invoices' => []];
|
$existingData = ['invoices' => []];
|
||||||
|
|
||||||
if (file_exists($dataFile)) {
|
if (file_exists($dataFile)) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -208,7 +208,7 @@ public function index(Request $request): View|Response
|
|||||||
private function flattenTree($categories): \Illuminate\Support\Collection
|
private function flattenTree($categories): \Illuminate\Support\Collection
|
||||||
{
|
{
|
||||||
$result = collect();
|
$result = collect();
|
||||||
$byParent = $categories->groupBy(fn($c) => $c->parent_id ?? 0);
|
$byParent = $categories->groupBy(fn ($c) => $c->parent_id ?? 0);
|
||||||
|
|
||||||
$this->addChildrenRecursive($result, $byParent, 0, 0);
|
$this->addChildrenRecursive($result, $byParent, 0, 0);
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class CategorySyncController extends Controller
|
|||||||
protected function getTenantId(): int
|
protected function getTenantId(): int
|
||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id');
|
$tenantId = session('selected_tenant_id');
|
||||||
|
|
||||||
return ($tenantId && $tenantId !== 'all') ? (int) $tenantId : 1;
|
return ($tenantId && $tenantId !== 'all') ? (int) $tenantId : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +156,7 @@ public function import(Request $request): JsonResponse
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skipped++;
|
$skipped++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +190,7 @@ public function import(Request $request): JsonResponse
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skipped++;
|
$skipped++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +221,7 @@ public function import(Request $request): JsonResponse
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => "{$imported}개 카테고리가 동기화되었습니다." . ($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
|
'message' => "{$imported}개 카테고리가 동기화되었습니다.".($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
|
||||||
'imported' => $imported,
|
'imported' => $imported,
|
||||||
'skipped' => $skipped,
|
'skipped' => $skipped,
|
||||||
]);
|
]);
|
||||||
@@ -247,6 +250,7 @@ public function push(Request $request): JsonResponse
|
|||||||
$localCategories = $this->getCategoryList($validated['type']);
|
$localCategories = $this->getCategoryList($validated['type']);
|
||||||
$selectedCategories = array_filter($localCategories, function ($cat) use ($validated) {
|
$selectedCategories = array_filter($localCategories, function ($cat) use ($validated) {
|
||||||
$key = $this->makeCategoryKey($cat);
|
$key = $this->makeCategoryKey($cat);
|
||||||
|
|
||||||
return in_array($key, $validated['category_keys']);
|
return in_array($key, $validated['category_keys']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -259,7 +263,7 @@ public function push(Request $request): JsonResponse
|
|||||||
$response = Http::withHeaders([
|
$response = Http::withHeaders([
|
||||||
'X-Menu-Sync-Key' => $env['api_key'],
|
'X-Menu-Sync-Key' => $env['api_key'],
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
])->post(rtrim($env['url'], '/') . '/category-sync/import', [
|
])->post(rtrim($env['url'], '/').'/category-sync/import', [
|
||||||
'categories' => array_values($selectedCategories),
|
'categories' => array_values($selectedCategories),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -276,7 +280,7 @@ public function push(Request $request): JsonResponse
|
|||||||
'error' => $response->json('error', '원격 서버 오류'),
|
'error' => $response->json('error', '원격 서버 오류'),
|
||||||
], $response->status());
|
], $response->status());
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return response()->json(['error' => '연결 실패: ' . $e->getMessage()], 500);
|
return response()->json(['error' => '연결 실패: '.$e->getMessage()], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,6 +313,7 @@ public function pull(Request $request): JsonResponse
|
|||||||
// 선택된 카테고리만 필터링
|
// 선택된 카테고리만 필터링
|
||||||
$selectedCategories = array_filter($remoteCategories, function ($cat) use ($validated) {
|
$selectedCategories = array_filter($remoteCategories, function ($cat) use ($validated) {
|
||||||
$key = $this->makeCategoryKey($cat);
|
$key = $this->makeCategoryKey($cat);
|
||||||
|
|
||||||
return in_array($key, $validated['category_keys']);
|
return in_array($key, $validated['category_keys']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -328,6 +333,7 @@ public function pull(Request $request): JsonResponse
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skipped++;
|
$skipped++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,6 +365,7 @@ public function pull(Request $request): JsonResponse
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skipped++;
|
$skipped++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +395,7 @@ public function pull(Request $request): JsonResponse
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => "{$imported}개 카테고리가 동기화되었습니다." . ($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
|
'message' => "{$imported}개 카테고리가 동기화되었습니다.".($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
|
||||||
'imported' => $imported,
|
'imported' => $imported,
|
||||||
'skipped' => $skipped,
|
'skipped' => $skipped,
|
||||||
]);
|
]);
|
||||||
@@ -396,6 +403,7 @@ public function pull(Request $request): JsonResponse
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 카테고리 목록 조회
|
* 카테고리 목록 조회
|
||||||
|
*
|
||||||
* @param string $type 'global', 'tenant', or 'all'
|
* @param string $type 'global', 'tenant', or 'all'
|
||||||
*/
|
*/
|
||||||
private function getCategoryList(string $type = 'all'): array
|
private function getCategoryList(string $type = 'all'): array
|
||||||
@@ -472,12 +480,12 @@ private function fetchRemoteCategories(array $env, string $type = 'all'): array
|
|||||||
$response = Http::withHeaders([
|
$response = Http::withHeaders([
|
||||||
'X-Menu-Sync-Key' => $env['api_key'],
|
'X-Menu-Sync-Key' => $env['api_key'],
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
])->timeout(10)->get(rtrim($env['url'], '/') . '/category-sync/export', [
|
])->timeout(10)->get(rtrim($env['url'], '/').'/category-sync/export', [
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (! $response->successful()) {
|
if (! $response->successful()) {
|
||||||
throw new \Exception('API 오류: HTTP ' . $response->status());
|
throw new \Exception('API 오류: HTTP '.$response->status());
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $response->json();
|
$data = $response->json();
|
||||||
@@ -496,6 +504,7 @@ private function fetchRemoteCategories(array $env, string $type = 'all'): array
|
|||||||
private function makeCategoryKey(array $cat): string
|
private function makeCategoryKey(array $cat): string
|
||||||
{
|
{
|
||||||
$typePart = $cat['is_global'] ? 'global' : "tenant:{$cat['tenant_id']}";
|
$typePart = $cat['is_global'] ? 'global' : "tenant:{$cat['tenant_id']}";
|
||||||
|
|
||||||
return "{$typePart}:{$cat['code_group']}:{$cat['code']}";
|
return "{$typePart}:{$cat['code_group']}:{$cat['code']}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,8 +513,8 @@ private function makeCategoryKey(array $cat): string
|
|||||||
*/
|
*/
|
||||||
private function calculateDiff(array $localCategories, array $remoteCategories): array
|
private function calculateDiff(array $localCategories, array $remoteCategories): array
|
||||||
{
|
{
|
||||||
$localKeys = array_map(fn($c) => $this->makeCategoryKey($c), $localCategories);
|
$localKeys = array_map(fn ($c) => $this->makeCategoryKey($c), $localCategories);
|
||||||
$remoteKeys = array_map(fn($c) => $this->makeCategoryKey($c), $remoteCategories);
|
$remoteKeys = array_map(fn ($c) => $this->makeCategoryKey($c), $remoteCategories);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'local_only' => array_values(array_diff($localKeys, $remoteKeys)),
|
'local_only' => array_values(array_diff($localKeys, $remoteKeys)),
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class CommonCodeController extends Controller
|
class CommonCodeController extends Controller
|
||||||
@@ -272,6 +271,7 @@ public function update(Request $request, int $id): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '테넌트를 먼저 선택해주세요.'], 400);
|
return response()->json(['error' => '테넌트를 먼저 선택해주세요.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '테넌트를 먼저 선택해주세요.');
|
return redirect()->back()->with('error', '테넌트를 먼저 선택해주세요.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +282,7 @@ public function update(Request $request, int $id): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '코드를 찾을 수 없습니다.'], 404);
|
return response()->json(['error' => '코드를 찾을 수 없습니다.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '코드를 찾을 수 없습니다.');
|
return redirect()->back()->with('error', '코드를 찾을 수 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,6 +295,7 @@ public function update(Request $request, int $id): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '글로벌 코드는 본사만 수정할 수 있습니다.'], 403);
|
return response()->json(['error' => '글로벌 코드는 본사만 수정할 수 있습니다.'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '글로벌 코드는 본사만 수정할 수 있습니다.');
|
return redirect()->back()->with('error', '글로벌 코드는 본사만 수정할 수 있습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +304,7 @@ public function update(Request $request, int $id): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '다른 테넌트의 코드는 수정할 수 없습니다.'], 403);
|
return response()->json(['error' => '다른 테넌트의 코드는 수정할 수 없습니다.'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '다른 테넌트의 코드는 수정할 수 없습니다.');
|
return redirect()->back()->with('error', '다른 테넌트의 코드는 수정할 수 없습니다.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,6 +396,7 @@ public function bulkPromoteToGlobal(Request $request): RedirectResponse|JsonResp
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '본사 또는 슈퍼관리자만 글로벌로 복사할 수 있습니다.'], 403);
|
return response()->json(['error' => '본사 또는 슈퍼관리자만 글로벌로 복사할 수 있습니다.'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '본사 또는 슈퍼관리자만 글로벌로 복사할 수 있습니다.');
|
return redirect()->back()->with('error', '본사 또는 슈퍼관리자만 글로벌로 복사할 수 있습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,6 +433,7 @@ public function bulkPromoteToGlobal(Request $request): RedirectResponse|JsonResp
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skippedCount++;
|
$skippedCount++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,6 +452,7 @@ public function bulkPromoteToGlobal(Request $request): RedirectResponse|JsonResp
|
|||||||
DB::commit();
|
DB::commit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
|
|
||||||
return redirect()->back()->with('error', '복사 중 오류가 발생했습니다.');
|
return redirect()->back()->with('error', '복사 중 오류가 발생했습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,6 +477,7 @@ public function copy(Request $request, int $id): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '테넌트를 먼저 선택해주세요.'], 400);
|
return response()->json(['error' => '테넌트를 먼저 선택해주세요.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '테넌트를 먼저 선택해주세요.');
|
return redirect()->back()->with('error', '테넌트를 먼저 선택해주세요.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,6 +486,7 @@ public function copy(Request $request, int $id): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '글로벌 코드를 찾을 수 없습니다.'], 404);
|
return response()->json(['error' => '글로벌 코드를 찾을 수 없습니다.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '글로벌 코드를 찾을 수 없습니다.');
|
return redirect()->back()->with('error', '글로벌 코드를 찾을 수 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,6 +501,7 @@ public function copy(Request $request, int $id): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '이미 복사된 코드가 있습니다.'], 400);
|
return response()->json(['error' => '이미 복사된 코드가 있습니다.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '이미 복사된 코드가 있습니다.');
|
return redirect()->back()->with('error', '이미 복사된 코드가 있습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,6 +536,7 @@ public function bulkCopy(Request $request): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '테넌트를 먼저 선택해주세요.'], 400);
|
return response()->json(['error' => '테넌트를 먼저 선택해주세요.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '테넌트를 먼저 선택해주세요.');
|
return redirect()->back()->with('error', '테넌트를 먼저 선택해주세요.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,6 +548,7 @@ public function bulkCopy(Request $request): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '복사할 코드를 선택해주세요.'], 400);
|
return response()->json(['error' => '복사할 코드를 선택해주세요.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '복사할 코드를 선택해주세요.');
|
return redirect()->back()->with('error', '복사할 코드를 선택해주세요.');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -570,6 +581,7 @@ public function bulkCopy(Request $request): RedirectResponse|JsonResponse
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skippedCount++;
|
$skippedCount++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,6 +605,7 @@ public function bulkCopy(Request $request): RedirectResponse|JsonResponse
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '복사 중 오류가 발생했습니다.'], 500);
|
return response()->json(['error' => '복사 중 오류가 발생했습니다.'], 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '복사 중 오류가 발생했습니다.');
|
return redirect()->back()->with('error', '복사 중 오류가 발생했습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,6 +637,7 @@ public function promoteToGlobal(Request $request, int $id): RedirectResponse|Jso
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '본사 또는 슈퍼관리자만 글로벌로 복사할 수 있습니다.'], 403);
|
return response()->json(['error' => '본사 또는 슈퍼관리자만 글로벌로 복사할 수 있습니다.'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '본사 또는 슈퍼관리자만 글로벌로 복사할 수 있습니다.');
|
return redirect()->back()->with('error', '본사 또는 슈퍼관리자만 글로벌로 복사할 수 있습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,6 +646,7 @@ public function promoteToGlobal(Request $request, int $id): RedirectResponse|Jso
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '테넌트 코드를 찾을 수 없습니다.'], 404);
|
return response()->json(['error' => '테넌트 코드를 찾을 수 없습니다.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '테넌트 코드를 찾을 수 없습니다.');
|
return redirect()->back()->with('error', '테넌트 코드를 찾을 수 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,6 +661,7 @@ public function promoteToGlobal(Request $request, int $id): RedirectResponse|Jso
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '이미 동일한 글로벌 코드가 존재합니다.'], 400);
|
return response()->json(['error' => '이미 동일한 글로벌 코드가 존재합니다.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '이미 동일한 글로벌 코드가 존재합니다.');
|
return redirect()->back()->with('error', '이미 동일한 글로벌 코드가 존재합니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -680,6 +696,7 @@ public function destroy(Request $request, int $id): RedirectResponse|JsonRespons
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '테넌트를 먼저 선택해주세요.'], 400);
|
return response()->json(['error' => '테넌트를 먼저 선택해주세요.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '테넌트를 먼저 선택해주세요.');
|
return redirect()->back()->with('error', '테넌트를 먼저 선택해주세요.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -690,6 +707,7 @@ public function destroy(Request $request, int $id): RedirectResponse|JsonRespons
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '코드를 찾을 수 없습니다.'], 404);
|
return response()->json(['error' => '코드를 찾을 수 없습니다.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '코드를 찾을 수 없습니다.');
|
return redirect()->back()->with('error', '코드를 찾을 수 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,6 +720,7 @@ public function destroy(Request $request, int $id): RedirectResponse|JsonRespons
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '글로벌 코드는 본사만 삭제할 수 있습니다.'], 403);
|
return response()->json(['error' => '글로벌 코드는 본사만 삭제할 수 있습니다.'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '글로벌 코드는 본사만 삭제할 수 있습니다.');
|
return redirect()->back()->with('error', '글로벌 코드는 본사만 삭제할 수 있습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,6 +729,7 @@ public function destroy(Request $request, int $id): RedirectResponse|JsonRespons
|
|||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response()->json(['error' => '다른 테넌트의 코드는 삭제할 수 없습니다.'], 403);
|
return response()->json(['error' => '다른 테넌트의 코드는 삭제할 수 없습니다.'], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()->with('error', '다른 테넌트의 코드는 삭제할 수 없습니다.');
|
return redirect()->back()->with('error', '다른 테넌트의 코드는 삭제할 수 없습니다.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class CommonCodeSyncController extends Controller
|
|||||||
protected function getTenantId(): int
|
protected function getTenantId(): int
|
||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id');
|
$tenantId = session('selected_tenant_id');
|
||||||
|
|
||||||
return ($tenantId && $tenantId !== 'all') ? (int) $tenantId : 1;
|
return ($tenantId && $tenantId !== 'all') ? (int) $tenantId : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +154,7 @@ public function import(Request $request): JsonResponse
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skipped++;
|
$skipped++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +172,7 @@ public function import(Request $request): JsonResponse
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => "{$imported}개 코드가 동기화되었습니다." . ($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
|
'message' => "{$imported}개 코드가 동기화되었습니다.".($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
|
||||||
'imported' => $imported,
|
'imported' => $imported,
|
||||||
'skipped' => $skipped,
|
'skipped' => $skipped,
|
||||||
]);
|
]);
|
||||||
@@ -199,6 +201,7 @@ public function push(Request $request): JsonResponse
|
|||||||
$localCodes = $this->getCodeList($validated['type']);
|
$localCodes = $this->getCodeList($validated['type']);
|
||||||
$selectedCodes = array_filter($localCodes, function ($code) use ($validated) {
|
$selectedCodes = array_filter($localCodes, function ($code) use ($validated) {
|
||||||
$key = $this->makeCodeKey($code);
|
$key = $this->makeCodeKey($code);
|
||||||
|
|
||||||
return in_array($key, $validated['code_keys']);
|
return in_array($key, $validated['code_keys']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -211,7 +214,7 @@ public function push(Request $request): JsonResponse
|
|||||||
$response = Http::withHeaders([
|
$response = Http::withHeaders([
|
||||||
'X-Menu-Sync-Key' => $env['api_key'],
|
'X-Menu-Sync-Key' => $env['api_key'],
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
])->post(rtrim($env['url'], '/') . '/common-code-sync/import', [
|
])->post(rtrim($env['url'], '/').'/common-code-sync/import', [
|
||||||
'codes' => array_values($selectedCodes),
|
'codes' => array_values($selectedCodes),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -228,7 +231,7 @@ public function push(Request $request): JsonResponse
|
|||||||
'error' => $response->json('error', '원격 서버 오류'),
|
'error' => $response->json('error', '원격 서버 오류'),
|
||||||
], $response->status());
|
], $response->status());
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return response()->json(['error' => '연결 실패: ' . $e->getMessage()], 500);
|
return response()->json(['error' => '연결 실패: '.$e->getMessage()], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +264,7 @@ public function pull(Request $request): JsonResponse
|
|||||||
// 선택된 코드만 필터링
|
// 선택된 코드만 필터링
|
||||||
$selectedCodes = array_filter($remoteCodes, function ($code) use ($validated) {
|
$selectedCodes = array_filter($remoteCodes, function ($code) use ($validated) {
|
||||||
$key = $this->makeCodeKey($code);
|
$key = $this->makeCodeKey($code);
|
||||||
|
|
||||||
return in_array($key, $validated['code_keys']);
|
return in_array($key, $validated['code_keys']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -282,6 +286,7 @@ public function pull(Request $request): JsonResponse
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skipped++;
|
$skipped++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +304,7 @@ public function pull(Request $request): JsonResponse
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => "{$imported}개 코드가 동기화되었습니다." . ($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
|
'message' => "{$imported}개 코드가 동기화되었습니다.".($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
|
||||||
'imported' => $imported,
|
'imported' => $imported,
|
||||||
'skipped' => $skipped,
|
'skipped' => $skipped,
|
||||||
]);
|
]);
|
||||||
@@ -307,6 +312,7 @@ public function pull(Request $request): JsonResponse
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 코드 목록 조회
|
* 코드 목록 조회
|
||||||
|
*
|
||||||
* @param string $type 'global', 'tenant', or 'all'
|
* @param string $type 'global', 'tenant', or 'all'
|
||||||
*/
|
*/
|
||||||
private function getCodeList(string $type = 'all'): array
|
private function getCodeList(string $type = 'all'): array
|
||||||
@@ -369,12 +375,12 @@ private function fetchRemoteCodes(array $env, string $type = 'all'): array
|
|||||||
$response = Http::withHeaders([
|
$response = Http::withHeaders([
|
||||||
'X-Menu-Sync-Key' => $env['api_key'],
|
'X-Menu-Sync-Key' => $env['api_key'],
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
])->timeout(10)->get(rtrim($env['url'], '/') . '/common-code-sync/export', [
|
])->timeout(10)->get(rtrim($env['url'], '/').'/common-code-sync/export', [
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (! $response->successful()) {
|
if (! $response->successful()) {
|
||||||
throw new \Exception('API 오류: HTTP ' . $response->status());
|
throw new \Exception('API 오류: HTTP '.$response->status());
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $response->json();
|
$data = $response->json();
|
||||||
@@ -393,6 +399,7 @@ private function fetchRemoteCodes(array $env, string $type = 'all'): array
|
|||||||
private function makeCodeKey(array $code): string
|
private function makeCodeKey(array $code): string
|
||||||
{
|
{
|
||||||
$tenantPart = $code['tenant_id'] ?? 'global';
|
$tenantPart = $code['tenant_id'] ?? 'global';
|
||||||
|
|
||||||
return "{$tenantPart}:{$code['code_group']}:{$code['code']}";
|
return "{$tenantPart}:{$code['code_group']}:{$code['code']}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,8 +408,8 @@ private function makeCodeKey(array $code): string
|
|||||||
*/
|
*/
|
||||||
private function calculateDiff(array $localCodes, array $remoteCodes): array
|
private function calculateDiff(array $localCodes, array $remoteCodes): array
|
||||||
{
|
{
|
||||||
$localKeys = array_map(fn($c) => $this->makeCodeKey($c), $localCodes);
|
$localKeys = array_map(fn ($c) => $this->makeCodeKey($c), $localCodes);
|
||||||
$remoteKeys = array_map(fn($c) => $this->makeCodeKey($c), $remoteCodes);
|
$remoteKeys = array_map(fn ($c) => $this->makeCodeKey($c), $remoteCodes);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'local_only' => array_values(array_diff($localKeys, $remoteKeys)),
|
'local_only' => array_values(array_diff($localKeys, $remoteKeys)),
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public function inquiry(Request $request): View|Response
|
|||||||
return response('', 200)->header('HX-Redirect', route('credit.inquiry.index'));
|
return response('', 200)->header('HX-Redirect', route('credit.inquiry.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$service = new CooconService();
|
$service = new CooconService;
|
||||||
$hasConfig = $service->hasConfig();
|
$hasConfig = $service->hasConfig();
|
||||||
|
|
||||||
// 검색 조건
|
// 검색 조건
|
||||||
@@ -42,10 +42,10 @@ public function inquiry(Request $request): View|Response
|
|||||||
|
|
||||||
// 기간 검색
|
// 기간 검색
|
||||||
if ($request->filled('start_date')) {
|
if ($request->filled('start_date')) {
|
||||||
$query->where('inquired_at', '>=', $request->input('start_date') . ' 00:00:00');
|
$query->where('inquired_at', '>=', $request->input('start_date').' 00:00:00');
|
||||||
}
|
}
|
||||||
if ($request->filled('end_date')) {
|
if ($request->filled('end_date')) {
|
||||||
$query->where('inquired_at', '<=', $request->input('end_date') . ' 23:59:59');
|
$query->where('inquired_at', '<=', $request->input('end_date').' 23:59:59');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 이슈 있는 것만
|
// 이슈 있는 것만
|
||||||
@@ -79,9 +79,9 @@ public function search(Request $request): JsonResponse
|
|||||||
|
|
||||||
$companyKey = preg_replace('/[^0-9]/', '', $request->input('company_key'));
|
$companyKey = preg_replace('/[^0-9]/', '', $request->input('company_key'));
|
||||||
|
|
||||||
$cooconService = new CooconService();
|
$cooconService = new CooconService;
|
||||||
|
|
||||||
if (!$cooconService->hasConfig()) {
|
if (! $cooconService->hasConfig()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => '쿠콘 API 설정이 없습니다. 설정을 먼저 등록해주세요.',
|
'error' => '쿠콘 API 설정이 없습니다. 설정을 먼저 등록해주세요.',
|
||||||
@@ -92,7 +92,7 @@ public function search(Request $request): JsonResponse
|
|||||||
$apiResult = $cooconService->getAllCreditInfo($companyKey);
|
$apiResult = $cooconService->getAllCreditInfo($companyKey);
|
||||||
|
|
||||||
// 국세청 사업자등록 상태 조회
|
// 국세청 사업자등록 상태 조회
|
||||||
$ntsService = new NtsBusinessService();
|
$ntsService = new NtsBusinessService;
|
||||||
$ntsResult = $ntsService->getBusinessStatus($companyKey);
|
$ntsResult = $ntsService->getBusinessStatus($companyKey);
|
||||||
|
|
||||||
// DB에 저장 (tenant_id는 세션에서 가져옴)
|
// DB에 저장 (tenant_id는 세션에서 가져옴)
|
||||||
@@ -354,14 +354,14 @@ public function toggleConfig(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$config = CooconConfig::findOrFail($id);
|
$config = CooconConfig::findOrFail($id);
|
||||||
|
|
||||||
if (!$config->is_active) {
|
if (! $config->is_active) {
|
||||||
// 같은 환경에서 활성화된 설정이 이미 있으면 비활성화
|
// 같은 환경에서 활성화된 설정이 이미 있으면 비활성화
|
||||||
CooconConfig::where('environment', $config->environment)
|
CooconConfig::where('environment', $config->environment)
|
||||||
->where('is_active', true)
|
->where('is_active', true)
|
||||||
->update(['is_active' => false]);
|
->update(['is_active' => false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$config->update(['is_active' => !$config->is_active]);
|
$config->update(['is_active' => ! $config->is_active]);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
@@ -380,9 +380,9 @@ public function testConnection(Request $request): JsonResponse
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$companyKey = $request->input('company_key');
|
$companyKey = $request->input('company_key');
|
||||||
$service = new CooconService();
|
$service = new CooconService;
|
||||||
|
|
||||||
if (!$service->hasConfig()) {
|
if (! $service->hasConfig()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'error' => '쿠콘 API 설정이 없습니다.',
|
'error' => '쿠콘 API 설정이 없습니다.',
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ public function index(Request $request): View|Response
|
|||||||
$startDate = "{$year}-01-01 00:00:00";
|
$startDate = "{$year}-01-01 00:00:00";
|
||||||
$endDate = "{$year}-12-31 23:59:59";
|
$endDate = "{$year}-12-31 23:59:59";
|
||||||
} elseif ($viewType === 'custom') {
|
} elseif ($viewType === 'custom') {
|
||||||
$startDate = $request->input('start_date', date('Y-m-01')) . ' 00:00:00';
|
$startDate = $request->input('start_date', date('Y-m-01')).' 00:00:00';
|
||||||
$endDate = $request->input('end_date', date('Y-m-t')) . ' 23:59:59';
|
$endDate = $request->input('end_date', date('Y-m-t')).' 23:59:59';
|
||||||
} else {
|
} else {
|
||||||
$startDate = "{$year}-{$month}-01 00:00:00";
|
$startDate = "{$year}-{$month}-01 00:00:00";
|
||||||
$endDate = date('Y-m-t 23:59:59', strtotime($startDate));
|
$endDate = date('Y-m-t 23:59:59', strtotime($startDate));
|
||||||
@@ -116,7 +116,7 @@ private function getAllTenantsUsage(string $startDate, string $endDate, string $
|
|||||||
$tenantId = $row->tenant_id;
|
$tenantId = $row->tenant_id;
|
||||||
$month = $row->month;
|
$month = $row->month;
|
||||||
|
|
||||||
if (!isset($monthlyData[$tenantId])) {
|
if (! isset($monthlyData[$tenantId])) {
|
||||||
$monthlyData[$tenantId] = [];
|
$monthlyData[$tenantId] = [];
|
||||||
}
|
}
|
||||||
$monthlyData[$tenantId][$month] = $row->total_count;
|
$monthlyData[$tenantId][$month] = $row->total_count;
|
||||||
@@ -173,7 +173,7 @@ private function getAllTenantsUsage(string $startDate, string $endDate, string $
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 정렬: 조회 건수 내림차순
|
// 정렬: 조회 건수 내림차순
|
||||||
usort($details, fn($a, $b) => $b['count'] - $a['count']);
|
usort($details, fn ($a, $b) => $b['count'] - $a['count']);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'total_count' => $totalCount,
|
'total_count' => $totalCount,
|
||||||
@@ -228,7 +228,7 @@ private function getSingleTenantUsage(int $tenantId, string $startDate, string $
|
|||||||
$existingMonths = collect($details)->pluck('month')->toArray();
|
$existingMonths = collect($details)->pluck('month')->toArray();
|
||||||
for ($m = 1; $m <= 12; $m++) {
|
for ($m = 1; $m <= 12; $m++) {
|
||||||
$monthKey = sprintf('%s-%02d', $year, $m);
|
$monthKey = sprintf('%s-%02d', $year, $m);
|
||||||
if (!in_array($monthKey, $existingMonths)) {
|
if (! in_array($monthKey, $existingMonths)) {
|
||||||
$details[] = [
|
$details[] = [
|
||||||
'tenant_id' => $tenantId,
|
'tenant_id' => $tenantId,
|
||||||
'tenant_name' => $tenant?->company_name ?? '(삭제됨)',
|
'tenant_name' => $tenant?->company_name ?? '(삭제됨)',
|
||||||
@@ -241,7 +241,7 @@ private function getSingleTenantUsage(int $tenantId, string $startDate, string $
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 월 순서로 정렬
|
// 월 순서로 정렬
|
||||||
usort($details, fn($a, $b) => strcmp($a['month'], $b['month']));
|
usort($details, fn ($a, $b) => strcmp($a['month'], $b['month']));
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -257,6 +257,7 @@ private function getSingleTenantUsage(int $tenantId, string $startDate, string $
|
|||||||
private function calculateFee(int $count): int
|
private function calculateFee(int $count): int
|
||||||
{
|
{
|
||||||
$paidCount = max(0, $count - self::FREE_MONTHLY_QUOTA);
|
$paidCount = max(0, $count - self::FREE_MONTHLY_QUOTA);
|
||||||
|
|
||||||
return $paidCount * self::ADDITIONAL_FEE_PER_INQUIRY;
|
return $paidCount * self::ADDITIONAL_FEE_PER_INQUIRY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,9 @@
|
|||||||
use App\Models\System\Schedule;
|
use App\Models\System\Schedule;
|
||||||
use App\Services\GoogleCloudStorageService;
|
use App\Services\GoogleCloudStorageService;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
@@ -174,7 +173,7 @@ public function uploadFiles(Request $request, int $scheduleId, GoogleCloudStorag
|
|||||||
|
|
||||||
foreach ($request->file('files') as $file) {
|
foreach ($request->file('files') as $file) {
|
||||||
$originalName = $file->getClientOriginalName();
|
$originalName = $file->getClientOriginalName();
|
||||||
$storedName = Str::random(40) . '.' . $file->getClientOriginalExtension();
|
$storedName = Str::random(40).'.'.$file->getClientOriginalExtension();
|
||||||
$storagePath = "schedules/{$tenantId}/{$schedule->id}/{$storedName}";
|
$storagePath = "schedules/{$tenantId}/{$schedule->id}/{$storedName}";
|
||||||
|
|
||||||
// 로컬(tenant 디스크) 저장
|
// 로컬(tenant 디스크) 저장
|
||||||
@@ -215,7 +214,7 @@ public function uploadFiles(Request $request, int $scheduleId, GoogleCloudStorag
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => count($uploaded) . '개 파일이 업로드되었습니다.',
|
'message' => count($uploaded).'개 파일이 업로드되었습니다.',
|
||||||
'files' => $uploaded,
|
'files' => $uploaded,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -274,9 +273,16 @@ public function downloadFile(int $scheduleId, int $fileId)
|
|||||||
*/
|
*/
|
||||||
private function determineFileType(string $mimeType): string
|
private function determineFileType(string $mimeType): string
|
||||||
{
|
{
|
||||||
if (str_starts_with($mimeType, 'image/')) return 'image';
|
if (str_starts_with($mimeType, 'image/')) {
|
||||||
if (str_contains($mimeType, 'spreadsheet') || str_contains($mimeType, 'excel')) return 'excel';
|
return 'image';
|
||||||
if (str_contains($mimeType, 'zip') || str_contains($mimeType, 'rar') || str_contains($mimeType, 'archive')) return 'archive';
|
}
|
||||||
|
if (str_contains($mimeType, 'spreadsheet') || str_contains($mimeType, 'excel')) {
|
||||||
|
return 'excel';
|
||||||
|
}
|
||||||
|
if (str_contains($mimeType, 'zip') || str_contains($mimeType, 'rar') || str_contains($mimeType, 'archive')) {
|
||||||
|
return 'archive';
|
||||||
|
}
|
||||||
|
|
||||||
return 'document';
|
return 'document';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -461,13 +461,13 @@ public function users(): JsonResponse
|
|||||||
// 세션에서 직접 테넌트 ID 조회 (관리자가 선택한 테넌트)
|
// 세션에서 직접 테넌트 ID 조회 (관리자가 선택한 테넌트)
|
||||||
$selectedTenantId = session('selected_tenant_id');
|
$selectedTenantId = session('selected_tenant_id');
|
||||||
|
|
||||||
if (!$selectedTenantId) {
|
if (! $selectedTenantId) {
|
||||||
// 테넌트가 선택되지 않은 경우 로그인 사용자의 기본 테넌트 사용
|
// 테넌트가 선택되지 않은 경우 로그인 사용자의 기본 테넌트 사용
|
||||||
$currentTenant = auth()->user()->tenants()
|
$currentTenant = auth()->user()->tenants()
|
||||||
->where('is_default', true)
|
->where('is_default', true)
|
||||||
->first() ?? auth()->user()->tenants()->first();
|
->first() ?? auth()->user()->tenants()->first();
|
||||||
|
|
||||||
if (!$currentTenant) {
|
if (! $currentTenant) {
|
||||||
return response()->json([]);
|
return response()->json([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,7 +477,7 @@ public function users(): JsonResponse
|
|||||||
// Tenant 모델에서 직접 조회 (사용자의 테넌트 관계와 무관하게)
|
// Tenant 모델에서 직접 조회 (사용자의 테넌트 관계와 무관하게)
|
||||||
$tenant = \App\Models\Tenants\Tenant::find($selectedTenantId);
|
$tenant = \App\Models\Tenants\Tenant::find($selectedTenantId);
|
||||||
|
|
||||||
if (!$tenant) {
|
if (! $tenant) {
|
||||||
return response()->json([]);
|
return response()->json([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +510,7 @@ public function issueToken(Request $request): JsonResponse
|
|||||||
|
|
||||||
$user = \App\Models\User::find($validated['user_id']);
|
$user = \App\Models\User::find($validated['user_id']);
|
||||||
|
|
||||||
if (!$user) {
|
if (! $user) {
|
||||||
return response()->json(['error' => '사용자를 찾을 수 없습니다.'], 404);
|
return response()->json(['error' => '사용자를 찾을 수 없습니다.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -248,6 +248,7 @@ public function show(int $id): View
|
|||||||
->filter(fn ($lot) => $lot->total_qty < 0)
|
->filter(fn ($lot) => $lot->total_qty < 0)
|
||||||
->map(function ($lot) {
|
->map(function ($lot) {
|
||||||
$lot->total_qty = abs($lot->total_qty);
|
$lot->total_qty = abs($lot->total_qty);
|
||||||
|
|
||||||
return $lot;
|
return $lot;
|
||||||
})
|
})
|
||||||
->values();
|
->values();
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ private function resolveDisplayText(?string $sourceTable, int $linkableId, ?arra
|
|||||||
$title = is_object($record) ? ($record->$titleField ?? '') : ($record->$titleField ?? '');
|
$title = is_object($record) ? ($record->$titleField ?? '') : ($record->$titleField ?? '');
|
||||||
$subtitle = $subtitleField ? (is_object($record) ? ($record->$subtitleField ?? '') : ($record->$subtitleField ?? '')) : '';
|
$subtitle = $subtitleField ? (is_object($record) ? ($record->$subtitleField ?? '') : ($record->$subtitleField ?? '')) : '';
|
||||||
|
|
||||||
return $title . ($subtitle ? " ({$subtitle})" : '');
|
return $title.($subtitle ? " ({$subtitle})" : '');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return "ID: {$linkableId}";
|
return "ID: {$linkableId}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public function index(Request $request): JsonResponse
|
|||||||
'total' => $transactions->count(),
|
'total' => $transactions->count(),
|
||||||
'totalAmount' => $transactions->sum('amount'),
|
'totalAmount' => $transactions->sum('amount'),
|
||||||
'approvedAmount' => $transactions->where('status', 'approved')->sum('amount'),
|
'approvedAmount' => $transactions->where('status', 'approved')->sum('amount'),
|
||||||
'cancelledAmount' => $transactions->where('status', 'cancelled')->sum(fn($t) => abs($t['amount'])),
|
'cancelledAmount' => $transactions->where('status', 'cancelled')->sum(fn ($t) => abs($t['amount'])),
|
||||||
];
|
];
|
||||||
|
|
||||||
// 카드 목록
|
// 카드 목록
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ public function index(Request $request): JsonResponse
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($status = $request->input('status')) {
|
if ($status = $request->input('status')) {
|
||||||
if ($status !== 'all') $query->where('status', $status);
|
if ($status !== 'all') {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$fees = $query->orderBy('date', 'desc')->get()->map(fn($item) => [
|
$fees = $query->orderBy('date', 'desc')->get()->map(fn ($item) => [
|
||||||
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
||||||
'consultant' => $item->consultant, 'customer' => $item->customer,
|
'consultant' => $item->consultant, 'customer' => $item->customer,
|
||||||
'service' => $item->service, 'hours' => $item->hours,
|
'service' => $item->service, 'hours' => $item->hours,
|
||||||
@@ -82,6 +84,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
ConsultingFee::forTenant($tenantId)->findOrFail($id)->delete();
|
ConsultingFee::forTenant($tenantId)->findOrFail($id)->delete();
|
||||||
|
|
||||||
return response()->json(['success' => true, 'message' => '컨설팅비가 삭제되었습니다.']);
|
return response()->json(['success' => true, 'message' => '컨설팅비가 삭제되었습니다.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ public function summary(): JsonResponse
|
|||||||
$adjustedDate = $this->getAdjustedPaymentDate($tenantId, $nextMonth->year, $nextMonth->month, $paymentDay);
|
$adjustedDate = $this->getAdjustedPaymentDate($tenantId, $nextMonth->year, $nextMonth->month, $paymentDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
$isAdjusted = !$originalDate->isSameDay($adjustedDate);
|
$isAdjusted = ! $originalDate->isSameDay($adjustedDate);
|
||||||
|
|
||||||
// 청구기간: 결제일 기준 전월 1일 ~ 결제일
|
// 청구기간: 결제일 기준 전월 1일 ~ 결제일
|
||||||
$billingStart = $adjustedDate->copy()->subMonth()->startOfMonth();
|
$billingStart = $adjustedDate->copy()->subMonth()->startOfMonth();
|
||||||
@@ -283,7 +283,7 @@ public function updatePrepayment(Request $request): JsonResponse
|
|||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
$yearMonth = $this->getBillingYearMonth($tenantId);
|
$yearMonth = $this->getBillingYearMonth($tenantId);
|
||||||
|
|
||||||
$items = collect($request->input('items', []))->filter(fn($item) => ($item['amount'] ?? 0) > 0)->values()->toArray();
|
$items = collect($request->input('items', []))->filter(fn ($item) => ($item['amount'] ?? 0) > 0)->values()->toArray();
|
||||||
$amount = collect($items)->sum('amount');
|
$amount = collect($items)->sum('amount');
|
||||||
|
|
||||||
$prepayment = CorporateCardPrepayment::updateOrCreate(
|
$prepayment = CorporateCardPrepayment::updateOrCreate(
|
||||||
@@ -328,6 +328,7 @@ private function getBillingYearMonth(int $tenantId): string
|
|||||||
private function createPaymentDate(int $year, int $month, int $day): Carbon
|
private function createPaymentDate(int $year, int $month, int $day): Carbon
|
||||||
{
|
{
|
||||||
$maxDay = Carbon::create($year, $month)->daysInMonth;
|
$maxDay = Carbon::create($year, $month)->daysInMonth;
|
||||||
|
|
||||||
return Carbon::create($year, $month, min($day, $maxDay));
|
return Carbon::create($year, $month, min($day, $maxDay));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,7 +368,7 @@ private function getAdjustedPaymentDate(int $tenantId, int $year, int $month, in
|
|||||||
private function calculateBillingUsage(int $tenantId, string $startDate, string $endDate, array $cardNumbers): array
|
private function calculateBillingUsage(int $tenantId, string $startDate, string $endDate, array $cardNumbers): array
|
||||||
{
|
{
|
||||||
// 카드번호 정규화 (하이픈 제거) + 원본↔정규화 매핑
|
// 카드번호 정규화 (하이픈 제거) + 원본↔정규화 매핑
|
||||||
$normalizedNums = array_map(fn($num) => str_replace('-', '', $num), $cardNumbers);
|
$normalizedNums = array_map(fn ($num) => str_replace('-', '', $num), $cardNumbers);
|
||||||
|
|
||||||
if (empty($normalizedNums)) {
|
if (empty($normalizedNums)) {
|
||||||
return ['total' => 0, 'perCard' => []];
|
return ['total' => 0, 'perCard' => []];
|
||||||
|
|||||||
@@ -24,13 +24,17 @@ public function index(Request $request): JsonResponse
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($status = $request->input('status')) {
|
if ($status = $request->input('status')) {
|
||||||
if ($status !== 'all') $query->where('status', $status);
|
if ($status !== 'all') {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($grade = $request->input('grade')) {
|
if ($grade = $request->input('grade')) {
|
||||||
if ($grade !== 'all') $query->where('grade', $grade);
|
if ($grade !== 'all') {
|
||||||
|
$query->where('grade', $grade);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$customers = $query->orderBy('created_at', 'desc')->get()->map(fn($item) => [
|
$customers = $query->orderBy('created_at', 'desc')->get()->map(fn ($item) => [
|
||||||
'id' => $item->id, 'name' => $item->name, 'bizNo' => $item->biz_no,
|
'id' => $item->id, 'name' => $item->name, 'bizNo' => $item->biz_no,
|
||||||
'ceo' => $item->ceo, 'industry' => $item->industry, 'grade' => $item->grade,
|
'ceo' => $item->ceo, 'industry' => $item->industry, 'grade' => $item->grade,
|
||||||
'contact' => $item->contact, 'email' => $item->email, 'address' => $item->address,
|
'contact' => $item->contact, 'email' => $item->email, 'address' => $item->address,
|
||||||
@@ -90,6 +94,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
Customer::forTenant($tenantId)->findOrFail($id)->delete();
|
Customer::forTenant($tenantId)->findOrFail($id)->delete();
|
||||||
|
|
||||||
return response()->json(['success' => true, 'message' => '고객사가 삭제되었습니다.']);
|
return response()->json(['success' => true, 'message' => '고객사가 삭제되었습니다.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,13 +135,14 @@ function ($attribute, $value, $fail) {
|
|||||||
return response()->json(['ok' => false, 'message' => $e->getMessage()], 500);
|
return response()->json(['ok' => false, 'message' => $e->getMessage()], 500);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
Log::error('고객사 OCR 예상치 못한 오류', ['error' => $e->getMessage()]);
|
Log::error('고객사 OCR 예상치 못한 오류', ['error' => $e->getMessage()]);
|
||||||
|
|
||||||
return response()->json(['ok' => false, 'message' => 'OCR 처리 중 오류가 발생했습니다.'], 500);
|
return response()->json(['ok' => false, 'message' => 'OCR 처리 중 오류가 발생했습니다.'], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function matchIndustry(string $bizType, string $bizItem): string
|
private function matchIndustry(string $bizType, string $bizItem): string
|
||||||
{
|
{
|
||||||
$text = $bizType . ' ' . $bizItem;
|
$text = $bizType.' '.$bizItem;
|
||||||
|
|
||||||
$keywords = [
|
$keywords = [
|
||||||
'IT/소프트웨어' => ['소프트웨어', 'IT', '정보통신', '전산', '컴퓨터', '인터넷', '데이터', '프로그램'],
|
'IT/소프트웨어' => ['소프트웨어', 'IT', '정보통신', '전산', '컴퓨터', '인터넷', '데이터', '프로그램'],
|
||||||
@@ -162,8 +168,13 @@ private function buildCustomerMemo(array $raw): string
|
|||||||
$parts = [];
|
$parts = [];
|
||||||
$bizType = trim($raw['biz_type'] ?? '');
|
$bizType = trim($raw['biz_type'] ?? '');
|
||||||
$bizItem = trim($raw['biz_item'] ?? '');
|
$bizItem = trim($raw['biz_item'] ?? '');
|
||||||
if ($bizType) $parts[] = "[업태] {$bizType}";
|
if ($bizType) {
|
||||||
if ($bizItem) $parts[] = "[종목] {$bizItem}";
|
$parts[] = "[업태] {$bizType}";
|
||||||
|
}
|
||||||
|
if ($bizItem) {
|
||||||
|
$parts[] = "[종목] {$bizItem}";
|
||||||
|
}
|
||||||
|
|
||||||
return implode(' / ', $parts);
|
return implode(' / ', $parts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ public function index(Request $request): JsonResponse
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($status = $request->input('status')) {
|
if ($status = $request->input('status')) {
|
||||||
if ($status !== 'all') $query->where('status', $status);
|
if ($status !== 'all') {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$settlements = $query->orderBy('created_at', 'desc')->get()->map(fn($item) => [
|
$settlements = $query->orderBy('created_at', 'desc')->get()->map(fn ($item) => [
|
||||||
'id' => $item->id, 'period' => $item->period,
|
'id' => $item->id, 'period' => $item->period,
|
||||||
'customer' => $item->customer, 'totalSales' => $item->total_sales,
|
'customer' => $item->customer, 'totalSales' => $item->total_sales,
|
||||||
'commission' => $item->commission, 'expense' => $item->expense,
|
'commission' => $item->commission, 'expense' => $item->expense,
|
||||||
@@ -85,6 +87,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
CustomerSettlement::forTenant($tenantId)->findOrFail($id)->delete();
|
CustomerSettlement::forTenant($tenantId)->findOrFail($id)->delete();
|
||||||
|
|
||||||
return response()->json(['success' => true, 'message' => '정산이 삭제되었습니다.']);
|
return response()->json(['success' => true, 'message' => '정산이 삭제되었습니다.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
namespace App\Http\Controllers\Finance;
|
namespace App\Http\Controllers\Finance;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Finance\DailyFundTransaction;
|
|
||||||
use App\Models\Finance\DailyFundMemo;
|
|
||||||
use App\Models\Barobill\BankTransaction as BarobillBankTransaction;
|
use App\Models\Barobill\BankTransaction as BarobillBankTransaction;
|
||||||
use App\Models\Barobill\BankTransactionOverride;
|
use App\Models\Barobill\BankTransactionOverride;
|
||||||
|
use App\Models\Finance\DailyFundMemo;
|
||||||
|
use App\Models\Finance\DailyFundTransaction;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class DailyFundController extends Controller
|
class DailyFundController extends Controller
|
||||||
{
|
{
|
||||||
@@ -179,14 +178,14 @@ public function periodReport(Request $request): JsonResponse
|
|||||||
->whereBetween('trans_date', [$startDateYmd, $endDateYmd])
|
->whereBetween('trans_date', [$startDateYmd, $endDateYmd])
|
||||||
->orderBy('id', 'desc') // 최신 ID 우선 (올바른 잔액)
|
->orderBy('id', 'desc') // 최신 ID 우선 (올바른 잔액)
|
||||||
->get()
|
->get()
|
||||||
->unique(fn($tx) => $tx->unique_key)
|
->unique(fn ($tx) => $tx->unique_key)
|
||||||
->sortByDesc('trans_date')
|
->sortByDesc('trans_date')
|
||||||
->sortByDesc(fn($tx) => $tx->trans_date . $tx->trans_time)
|
->sortByDesc(fn ($tx) => $tx->trans_date.$tx->trans_time)
|
||||||
->values();
|
->values();
|
||||||
|
|
||||||
// 오버라이드 데이터 병합 (수정된 적요/내용)
|
// 오버라이드 데이터 병합 (수정된 적요/내용)
|
||||||
if ($transactions->isNotEmpty()) {
|
if ($transactions->isNotEmpty()) {
|
||||||
$uniqueKeys = $transactions->map(fn($t) => $t->unique_key)->toArray();
|
$uniqueKeys = $transactions->map(fn ($t) => $t->unique_key)->toArray();
|
||||||
$overrides = BankTransactionOverride::getByUniqueKeys($tenantId, $uniqueKeys);
|
$overrides = BankTransactionOverride::getByUniqueKeys($tenantId, $uniqueKeys);
|
||||||
|
|
||||||
$transactions = $transactions->map(function ($tx) use ($overrides) {
|
$transactions = $transactions->map(function ($tx) use ($overrides) {
|
||||||
@@ -199,6 +198,7 @@ public function periodReport(Request $request): JsonResponse
|
|||||||
$tx->cast = $override->modified_cast;
|
$tx->cast = $override->modified_cast;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tx;
|
return $tx;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -211,7 +211,7 @@ public function periodReport(Request $request): JsonResponse
|
|||||||
$date = $tx->trans_date;
|
$date = $tx->trans_date;
|
||||||
$accountNum = $tx->bank_account_num;
|
$accountNum = $tx->bank_account_num;
|
||||||
|
|
||||||
if (!isset($dailyData[$date])) {
|
if (! isset($dailyData[$date])) {
|
||||||
$dailyData[$date] = [
|
$dailyData[$date] = [
|
||||||
'date' => $date,
|
'date' => $date,
|
||||||
'dateFormatted' => $this->formatDateKorean($date),
|
'dateFormatted' => $this->formatDateKorean($date),
|
||||||
@@ -224,7 +224,7 @@ public function periodReport(Request $request): JsonResponse
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 계좌별 데이터 집계
|
// 계좌별 데이터 집계
|
||||||
if (!isset($dailyData[$date]['accounts'][$accountNum])) {
|
if (! isset($dailyData[$date]['accounts'][$accountNum])) {
|
||||||
$dailyData[$date]['accounts'][$accountNum] = [
|
$dailyData[$date]['accounts'][$accountNum] = [
|
||||||
'bankName' => $tx->bank_name,
|
'bankName' => $tx->bank_name,
|
||||||
'accountNum' => $accountNum,
|
'accountNum' => $accountNum,
|
||||||
|
|||||||
@@ -21,13 +21,17 @@ public function index(Request $request): JsonResponse
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($status = $request->input('status')) {
|
if ($status = $request->input('status')) {
|
||||||
if ($status !== 'all') $query->where('status', $status);
|
if ($status !== 'all') {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($category = $request->input('category')) {
|
if ($category = $request->input('category')) {
|
||||||
if ($category !== 'all') $query->where('category', $category);
|
if ($category !== 'all') {
|
||||||
|
$query->where('category', $category);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$expenses = $query->orderBy('date', 'desc')->get()->map(fn($item) => [
|
$expenses = $query->orderBy('date', 'desc')->get()->map(fn ($item) => [
|
||||||
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
||||||
'vendor' => $item->vendor, 'description' => $item->description,
|
'vendor' => $item->vendor, 'description' => $item->description,
|
||||||
'category' => $item->category, 'amount' => $item->amount,
|
'category' => $item->category, 'amount' => $item->amount,
|
||||||
@@ -87,6 +91,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
Expense::forTenant($tenantId)->findOrFail($id)->delete();
|
Expense::forTenant($tenantId)->findOrFail($id)->delete();
|
||||||
|
|
||||||
return response()->json(['success' => true, 'message' => '지출이 삭제되었습니다.']);
|
return response()->json(['success' => true, 'message' => '지출이 삭제되었습니다.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
use App\Models\Barobill\BankTransactionOverride;
|
use App\Models\Barobill\BankTransactionOverride;
|
||||||
use App\Models\Barobill\CardTransaction as BarobillCardTransaction;
|
use App\Models\Barobill\CardTransaction as BarobillCardTransaction;
|
||||||
use App\Models\Finance\BankAccount;
|
use App\Models\Finance\BankAccount;
|
||||||
use App\Models\Finance\FundSchedule;
|
|
||||||
use App\Services\BankAccountService;
|
use App\Services\BankAccountService;
|
||||||
use App\Services\FundScheduleService;
|
use App\Services\FundScheduleService;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
@@ -53,7 +52,7 @@ public function index(): View
|
|||||||
|
|
||||||
// 오버라이드 데이터 병합 (수정된 적요/내용)
|
// 오버라이드 데이터 병합 (수정된 적요/내용)
|
||||||
if ($recentTransactions->isNotEmpty()) {
|
if ($recentTransactions->isNotEmpty()) {
|
||||||
$uniqueKeys = $recentTransactions->map(fn($t) => $t->unique_key)->toArray();
|
$uniqueKeys = $recentTransactions->map(fn ($t) => $t->unique_key)->toArray();
|
||||||
$overrides = BankTransactionOverride::getByUniqueKeys($tenantId, $uniqueKeys);
|
$overrides = BankTransactionOverride::getByUniqueKeys($tenantId, $uniqueKeys);
|
||||||
|
|
||||||
$recentTransactions = $recentTransactions->map(function ($transaction) use ($overrides) {
|
$recentTransactions = $recentTransactions->map(function ($transaction) use ($overrides) {
|
||||||
@@ -71,6 +70,7 @@ public function index(): View
|
|||||||
} else {
|
} else {
|
||||||
$transaction->is_overridden = false;
|
$transaction->is_overridden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $transaction;
|
return $transaction;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,18 @@ public function index(Request $request): JsonResponse
|
|||||||
->orWhere('description', 'like', "%{$search}%");
|
->orWhere('description', 'like', "%{$search}%");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($status = $request->input('status')) { if ($status !== 'all') $query->where('status', $status); }
|
if ($status = $request->input('status')) {
|
||||||
if ($category = $request->input('category')) { if ($category !== 'all') $query->where('category', $category); }
|
if ($status !== 'all') {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($category = $request->input('category')) {
|
||||||
|
if ($category !== 'all') {
|
||||||
|
$query->where('category', $category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$incomes = $query->orderBy('date', 'desc')->get()->map(fn($item) => [
|
$incomes = $query->orderBy('date', 'desc')->get()->map(fn ($item) => [
|
||||||
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
||||||
'customer' => $item->customer, 'description' => $item->description,
|
'customer' => $item->customer, 'description' => $item->description,
|
||||||
'category' => $item->category, 'amount' => $item->amount,
|
'category' => $item->category, 'amount' => $item->amount,
|
||||||
@@ -80,6 +88,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
Income::forTenant($tenantId)->findOrFail($id)->delete();
|
Income::forTenant($tenantId)->findOrFail($id)->delete();
|
||||||
|
|
||||||
return response()->json(['success' => true, 'message' => '수입이 삭제되었습니다.']);
|
return response()->json(['success' => true, 'message' => '수입이 삭제되었습니다.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,17 @@ public function index(Request $request): JsonResponse
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($status = $request->input('status')) {
|
if ($status = $request->input('status')) {
|
||||||
if ($status !== 'all') $query->where('status', $status);
|
if ($status !== 'all') {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($category = $request->input('category')) {
|
if ($category = $request->input('category')) {
|
||||||
if ($category !== 'all') $query->where('category', $category);
|
if ($category !== 'all') {
|
||||||
|
$query->where('category', $category);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$purchases = $query->orderBy('date', 'desc')->get()->map(fn($item) => [
|
$purchases = $query->orderBy('date', 'desc')->get()->map(fn ($item) => [
|
||||||
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
||||||
'vendor' => $item->vendor, 'item' => $item->item,
|
'vendor' => $item->vendor, 'item' => $item->item,
|
||||||
'category' => $item->category, 'amount' => $item->amount,
|
'category' => $item->category, 'amount' => $item->amount,
|
||||||
@@ -85,6 +89,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
Purchase::forTenant($tenantId)->findOrFail($id)->delete();
|
Purchase::forTenant($tenantId)->findOrFail($id)->delete();
|
||||||
|
|
||||||
return response()->json(['success' => true, 'message' => '매입이 삭제되었습니다.']);
|
return response()->json(['success' => true, 'message' => '매입이 삭제되었습니다.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public function __construct(
|
|||||||
public function index(Request $request): View|Response
|
public function index(Request $request): View|Response
|
||||||
{
|
{
|
||||||
// HTMX 요청 시 전체 페이지로 리다이렉트 (JavaScript 필요)
|
// HTMX 요청 시 전체 페이지로 리다이렉트 (JavaScript 필요)
|
||||||
if ($request->header('HX-Request') && !$request->header('HX-Boosted')) {
|
if ($request->header('HX-Request') && ! $request->header('HX-Boosted')) {
|
||||||
return response('', 200)->header('HX-Redirect', route('finance.sales-commissions.index'));
|
return response('', 200)->header('HX-Redirect', route('finance.sales-commissions.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ public function show(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$commission = $this->service->getCommissionById($id);
|
$commission = $this->service->getCommissionById($id);
|
||||||
|
|
||||||
if (!$commission) {
|
if (! $commission) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '정산 정보를 찾을 수 없습니다.',
|
'message' => '정산 정보를 찾을 수 없습니다.',
|
||||||
@@ -377,9 +377,9 @@ public function export(Request $request)
|
|||||||
// 전체 데이터 조회 (페이지네이션 없이)
|
// 전체 데이터 조회 (페이지네이션 없이)
|
||||||
$commissions = SalesCommission::query()
|
$commissions = SalesCommission::query()
|
||||||
->with(['tenant', 'partner.user', 'manager'])
|
->with(['tenant', 'partner.user', 'manager'])
|
||||||
->when(!empty($filters['status']), fn($q) => $q->where('status', $filters['status']))
|
->when(! empty($filters['status']), fn ($q) => $q->where('status', $filters['status']))
|
||||||
->when(!empty($filters['payment_type']), fn($q) => $q->where('payment_type', $filters['payment_type']))
|
->when(! empty($filters['payment_type']), fn ($q) => $q->where('payment_type', $filters['payment_type']))
|
||||||
->when(!empty($filters['partner_id']), fn($q) => $q->where('partner_id', $filters['partner_id']))
|
->when(! empty($filters['partner_id']), fn ($q) => $q->where('partner_id', $filters['partner_id']))
|
||||||
->forScheduledMonth($year, $month)
|
->forScheduledMonth($year, $month)
|
||||||
->orderBy('scheduled_payment_date')
|
->orderBy('scheduled_payment_date')
|
||||||
->get();
|
->get();
|
||||||
@@ -402,7 +402,7 @@ public function export(Request $request)
|
|||||||
fputcsv($file, [
|
fputcsv($file, [
|
||||||
'번호', '테넌트', '입금구분', '입금액', '입금일',
|
'번호', '테넌트', '입금구분', '입금액', '입금일',
|
||||||
'기준액', '영업파트너', '파트너수당', '매니저', '매니저수당',
|
'기준액', '영업파트너', '파트너수당', '매니저', '매니저수당',
|
||||||
'지급예정일', '상태', '실제지급일'
|
'지급예정일', '상태', '실제지급일',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 데이터
|
// 데이터
|
||||||
|
|||||||
@@ -21,13 +21,17 @@ public function index(Request $request): JsonResponse
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($status = $request->input('status')) {
|
if ($status = $request->input('status')) {
|
||||||
if ($status !== 'all') $query->where('status', $status);
|
if ($status !== 'all') {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($type = $request->input('type')) {
|
if ($type = $request->input('type')) {
|
||||||
if ($type !== 'all') $query->where('type', $type);
|
if ($type !== 'all') {
|
||||||
|
$query->where('type', $type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$records = $query->orderBy('date', 'desc')->get()->map(fn($item) => [
|
$records = $query->orderBy('date', 'desc')->get()->map(fn ($item) => [
|
||||||
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
'id' => $item->id, 'date' => $item->date?->format('Y-m-d'),
|
||||||
'customer' => $item->customer, 'project' => $item->project,
|
'customer' => $item->customer, 'project' => $item->project,
|
||||||
'type' => $item->type, 'taxType' => $item->tax_type ?? 'taxable',
|
'type' => $item->type, 'taxType' => $item->tax_type ?? 'taxable',
|
||||||
@@ -93,6 +97,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
SalesRecord::forTenant($tenantId)->findOrFail($id)->delete();
|
SalesRecord::forTenant($tenantId)->findOrFail($id)->delete();
|
||||||
|
|
||||||
return response()->json(['success' => true, 'message' => '매출이 삭제되었습니다.']);
|
return response()->json(['success' => true, 'message' => '매출이 삭제되었습니다.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ public function index(Request $request): JsonResponse
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ($status = $request->input('status')) {
|
if ($status = $request->input('status')) {
|
||||||
if ($status !== 'all') $query->where('status', $status);
|
if ($status !== 'all') {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$subscriptions = $query->orderBy('created_at', 'desc')->get()->map(fn($item) => [
|
$subscriptions = $query->orderBy('created_at', 'desc')->get()->map(fn ($item) => [
|
||||||
'id' => $item->id, 'customer' => $item->customer,
|
'id' => $item->id, 'customer' => $item->customer,
|
||||||
'plan' => $item->plan, 'monthlyFee' => $item->monthly_fee,
|
'plan' => $item->plan, 'monthlyFee' => $item->monthly_fee,
|
||||||
'billingCycle' => $item->billing_cycle,
|
'billingCycle' => $item->billing_cycle,
|
||||||
@@ -87,6 +89,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id', 1);
|
$tenantId = session('selected_tenant_id', 1);
|
||||||
Subscription::forTenant($tenantId)->findOrFail($id)->delete();
|
Subscription::forTenant($tenantId)->findOrFail($id)->delete();
|
||||||
|
|
||||||
return response()->json(['success' => true, 'message' => '구독이 삭제되었습니다.']);
|
return response()->json(['success' => true, 'message' => '구독이 삭제되었습니다.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
namespace App\Http\Controllers\Finance;
|
namespace App\Http\Controllers\Finance;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Finance\VatRecord;
|
|
||||||
use App\Models\Barobill\CardTransaction as BarobillCardTransaction;
|
use App\Models\Barobill\CardTransaction as BarobillCardTransaction;
|
||||||
use App\Models\Barobill\CardTransactionSplit;
|
|
||||||
use App\Models\Barobill\CardTransactionHide;
|
use App\Models\Barobill\CardTransactionHide;
|
||||||
|
use App\Models\Barobill\CardTransactionSplit;
|
||||||
use App\Models\Barobill\HometaxInvoice;
|
use App\Models\Barobill\HometaxInvoice;
|
||||||
|
use App\Models\Finance\VatRecord;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ public function index(Request $request): JsonResponse
|
|||||||
|
|
||||||
$hometaxSalesRecords = $hometaxSales->map(function ($inv) use ($period, $taxTypeMap) {
|
$hometaxSalesRecords = $hometaxSales->map(function ($inv) use ($period, $taxTypeMap) {
|
||||||
return [
|
return [
|
||||||
'id' => 'hometax_' . $inv->id,
|
'id' => 'hometax_'.$inv->id,
|
||||||
'period' => $period,
|
'period' => $period,
|
||||||
'type' => 'sales',
|
'type' => 'sales',
|
||||||
'taxType' => $taxTypeMap[$inv->tax_type] ?? 'taxable',
|
'taxType' => $taxTypeMap[$inv->tax_type] ?? 'taxable',
|
||||||
@@ -69,7 +69,7 @@ public function index(Request $request): JsonResponse
|
|||||||
|
|
||||||
$hometaxPurchaseRecords = $hometaxPurchases->map(function ($inv) use ($period, $taxTypeMap) {
|
$hometaxPurchaseRecords = $hometaxPurchases->map(function ($inv) use ($period, $taxTypeMap) {
|
||||||
return [
|
return [
|
||||||
'id' => 'hometax_' . $inv->id,
|
'id' => 'hometax_'.$inv->id,
|
||||||
'period' => $period,
|
'period' => $period,
|
||||||
'type' => 'purchase',
|
'type' => 'purchase',
|
||||||
'taxType' => $taxTypeMap[$inv->tax_type] ?? 'taxable',
|
'taxType' => $taxTypeMap[$inv->tax_type] ?? 'taxable',
|
||||||
@@ -104,7 +104,7 @@ public function index(Request $request): JsonResponse
|
|||||||
foreach ($splitsByKey as $fullKey => $splits) {
|
foreach ($splitsByKey as $fullKey => $splits) {
|
||||||
$parts = explode('|', $fullKey);
|
$parts = explode('|', $fullKey);
|
||||||
if (count($parts) >= 3) {
|
if (count($parts) >= 3) {
|
||||||
$partialKey = $parts[0] . '|' . $parts[1] . '|' . $parts[2];
|
$partialKey = $parts[0].'|'.$parts[1].'|'.$parts[2];
|
||||||
$splitsByPartialKey[$partialKey] = $splits;
|
$splitsByPartialKey[$partialKey] = $splits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,8 +117,8 @@ public function index(Request $request): JsonResponse
|
|||||||
|
|
||||||
// 분개 매칭: 정확한 키 → 부분키(금액 제외) 순으로 시도
|
// 분개 매칭: 정확한 키 → 부분키(금액 제외) 순으로 시도
|
||||||
$splits = $splitsByKey[$card->unique_key] ?? null;
|
$splits = $splitsByKey[$card->unique_key] ?? null;
|
||||||
if (!$splits) {
|
if (! $splits) {
|
||||||
$cardPartialKey = $card->card_num . '|' . $card->use_dt . '|' . $card->approval_num;
|
$cardPartialKey = $card->card_num.'|'.$card->use_dt.'|'.$card->approval_num;
|
||||||
$splits = $splitsByPartialKey[$cardPartialKey] ?? null;
|
$splits = $splitsByPartialKey[$cardPartialKey] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ public function index(Request $request): JsonResponse
|
|||||||
foreach ($splits as $split) {
|
foreach ($splits as $split) {
|
||||||
if ($split->deduction_type === 'deductible') {
|
if ($split->deduction_type === 'deductible') {
|
||||||
$cardRecords->push([
|
$cardRecords->push([
|
||||||
'id' => 'card_split_' . $split->id,
|
'id' => 'card_split_'.$split->id,
|
||||||
'period' => $period,
|
'period' => $period,
|
||||||
'type' => 'purchase',
|
'type' => 'purchase',
|
||||||
'taxType' => 'taxable',
|
'taxType' => 'taxable',
|
||||||
@@ -153,7 +153,7 @@ public function index(Request $request): JsonResponse
|
|||||||
$effectiveTax = $card->modified_tax ?? $card->tax;
|
$effectiveTax = $card->modified_tax ?? $card->tax;
|
||||||
|
|
||||||
$cardRecords->push([
|
$cardRecords->push([
|
||||||
'id' => 'card_' . $card->id,
|
'id' => 'card_'.$card->id,
|
||||||
'period' => $period,
|
'period' => $period,
|
||||||
'type' => 'purchase',
|
'type' => 'purchase',
|
||||||
'taxType' => 'taxable',
|
'taxType' => 'taxable',
|
||||||
@@ -269,13 +269,13 @@ public function index(Request $request): JsonResponse
|
|||||||
// 확정(C) 기간이면 대응하는 예정(P)의 netVat를 계산
|
// 확정(C) 기간이면 대응하는 예정(P)의 netVat를 계산
|
||||||
if ($period && str_ends_with($period, 'C')) {
|
if ($period && str_ends_with($period, 'C')) {
|
||||||
try {
|
try {
|
||||||
$prelimPeriod = substr($period, 0, -1) . 'P';
|
$prelimPeriod = substr($period, 0, -1).'P';
|
||||||
$stats['preliminaryVat'] = $this->calculatePeriodNetVat($tenantId, $prelimPeriod);
|
$stats['preliminaryVat'] = $this->calculatePeriodNetVat($tenantId, $prelimPeriod);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
\Log::error('예정 세액 계산 실패', [
|
\Log::error('예정 세액 계산 실패', [
|
||||||
'message' => $e->getMessage(),
|
'message' => $e->getMessage(),
|
||||||
'file' => $e->getFile() . ':' . $e->getLine(),
|
'file' => $e->getFile().':'.$e->getLine(),
|
||||||
'trace' => array_slice(array_map(fn($t) => ($t['file'] ?? '') . ':' . ($t['line'] ?? '') . ' ' . ($t['function'] ?? ''), $e->getTrace()), 0, 5),
|
'trace' => array_slice(array_map(fn ($t) => ($t['file'] ?? '').':'.($t['line'] ?? '').' '.($t['function'] ?? ''), $e->getTrace()), 0, 5),
|
||||||
'tenant_id' => $tenantId,
|
'tenant_id' => $tenantId,
|
||||||
'period' => $prelimPeriod ?? null,
|
'period' => $prelimPeriod ?? null,
|
||||||
]);
|
]);
|
||||||
@@ -385,7 +385,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
private function calculatePeriodNetVat(int $tenantId, string $period): int
|
private function calculatePeriodNetVat(int $tenantId, string $period): int
|
||||||
{
|
{
|
||||||
[$startDate, $endDate] = $this->periodToDateRange($period);
|
[$startDate, $endDate] = $this->periodToDateRange($period);
|
||||||
if (!$startDate || !$endDate) {
|
if (! $startDate || ! $endDate) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +422,7 @@ private function calculatePeriodNetVat(int $tenantId, string $period): int
|
|||||||
foreach ($splitsByKey as $fullKey => $splits) {
|
foreach ($splitsByKey as $fullKey => $splits) {
|
||||||
$parts = explode('|', $fullKey);
|
$parts = explode('|', $fullKey);
|
||||||
if (count($parts) >= 3) {
|
if (count($parts) >= 3) {
|
||||||
$partialKey = $parts[0] . '|' . $parts[1] . '|' . $parts[2];
|
$partialKey = $parts[0].'|'.$parts[1].'|'.$parts[2];
|
||||||
$splitsByPartialKey[$partialKey] = $splits;
|
$splitsByPartialKey[$partialKey] = $splits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,8 +433,8 @@ private function calculatePeriodNetVat(int $tenantId, string $period): int
|
|||||||
}
|
}
|
||||||
|
|
||||||
$splits = $splitsByKey[$card->unique_key] ?? null;
|
$splits = $splitsByKey[$card->unique_key] ?? null;
|
||||||
if (!$splits) {
|
if (! $splits) {
|
||||||
$cardPartialKey = $card->card_num . '|' . $card->use_dt . '|' . $card->approval_num;
|
$cardPartialKey = $card->card_num.'|'.$card->use_dt.'|'.$card->approval_num;
|
||||||
$splits = $splitsByPartialKey[$cardPartialKey] ?? null;
|
$splits = $splitsByPartialKey[$cardPartialKey] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public function show(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$photo = ConstructionSitePhoto::with(['user', 'rows'])->find($id);
|
$photo = ConstructionSitePhoto::with(['user', 'rows'])->find($id);
|
||||||
|
|
||||||
if (!$photo) {
|
if (! $photo) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진대지를 찾을 수 없습니다.',
|
'message' => '사진대지를 찾을 수 없습니다.',
|
||||||
@@ -77,7 +77,7 @@ public function uploadPhoto(Request $request, int $id, int $rowId): JsonResponse
|
|||||||
{
|
{
|
||||||
$photo = ConstructionSitePhoto::find($id);
|
$photo = ConstructionSitePhoto::find($id);
|
||||||
|
|
||||||
if (!$photo) {
|
if (! $photo) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진대지를 찾을 수 없습니다.',
|
'message' => '사진대지를 찾을 수 없습니다.',
|
||||||
@@ -88,7 +88,7 @@ public function uploadPhoto(Request $request, int $id, int $rowId): JsonResponse
|
|||||||
->where('construction_site_photo_id', $id)
|
->where('construction_site_photo_id', $id)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if (!$row) {
|
if (! $row) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진 행을 찾을 수 없습니다.',
|
'message' => '사진 행을 찾을 수 없습니다.',
|
||||||
@@ -102,7 +102,7 @@ public function uploadPhoto(Request $request, int $id, int $rowId): JsonResponse
|
|||||||
|
|
||||||
$result = $this->service->uploadPhoto($row, $request->file('photo'), $validated['type']);
|
$result = $this->service->uploadPhoto($row, $request->file('photo'), $validated['type']);
|
||||||
|
|
||||||
if (!$result) {
|
if (! $result) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진 업로드에 실패했습니다.',
|
'message' => '사진 업로드에 실패했습니다.',
|
||||||
@@ -120,7 +120,7 @@ public function update(Request $request, int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$photo = ConstructionSitePhoto::find($id);
|
$photo = ConstructionSitePhoto::find($id);
|
||||||
|
|
||||||
if (!$photo) {
|
if (! $photo) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진대지를 찾을 수 없습니다.',
|
'message' => '사진대지를 찾을 수 없습니다.',
|
||||||
@@ -146,7 +146,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$photo = ConstructionSitePhoto::with('rows')->find($id);
|
$photo = ConstructionSitePhoto::with('rows')->find($id);
|
||||||
|
|
||||||
if (!$photo) {
|
if (! $photo) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진대지를 찾을 수 없습니다.',
|
'message' => '사진대지를 찾을 수 없습니다.',
|
||||||
@@ -165,7 +165,7 @@ public function deletePhoto(int $id, int $rowId, string $type): JsonResponse
|
|||||||
{
|
{
|
||||||
$photo = ConstructionSitePhoto::find($id);
|
$photo = ConstructionSitePhoto::find($id);
|
||||||
|
|
||||||
if (!$photo) {
|
if (! $photo) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진대지를 찾을 수 없습니다.',
|
'message' => '사진대지를 찾을 수 없습니다.',
|
||||||
@@ -176,14 +176,14 @@ public function deletePhoto(int $id, int $rowId, string $type): JsonResponse
|
|||||||
->where('construction_site_photo_id', $id)
|
->where('construction_site_photo_id', $id)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if (!$row) {
|
if (! $row) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진 행을 찾을 수 없습니다.',
|
'message' => '사진 행을 찾을 수 없습니다.',
|
||||||
], 404);
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in_array($type, ['before', 'during', 'after'])) {
|
if (! in_array($type, ['before', 'during', 'after'])) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '올바르지 않은 사진 유형입니다.',
|
'message' => '올바르지 않은 사진 유형입니다.',
|
||||||
@@ -203,7 +203,7 @@ public function downloadPhoto(Request $request, int $id, int $rowId, string $typ
|
|||||||
{
|
{
|
||||||
$photo = ConstructionSitePhoto::find($id);
|
$photo = ConstructionSitePhoto::find($id);
|
||||||
|
|
||||||
if (!$photo) {
|
if (! $photo) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진대지를 찾을 수 없습니다.',
|
'message' => '사진대지를 찾을 수 없습니다.',
|
||||||
@@ -214,23 +214,23 @@ public function downloadPhoto(Request $request, int $id, int $rowId, string $typ
|
|||||||
->where('construction_site_photo_id', $id)
|
->where('construction_site_photo_id', $id)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if (!$row) {
|
if (! $row) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진 행을 찾을 수 없습니다.',
|
'message' => '사진 행을 찾을 수 없습니다.',
|
||||||
], 404);
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in_array($type, ['before', 'during', 'after'])) {
|
if (! in_array($type, ['before', 'during', 'after'])) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '올바르지 않은 사진 유형입니다.',
|
'message' => '올바르지 않은 사진 유형입니다.',
|
||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$path = $row->{$type . '_photo_path'};
|
$path = $row->{$type.'_photo_path'};
|
||||||
|
|
||||||
if (!$path) {
|
if (! $path) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '파일을 찾을 수 없습니다.',
|
'message' => '파일을 찾을 수 없습니다.',
|
||||||
@@ -240,7 +240,7 @@ public function downloadPhoto(Request $request, int $id, int $rowId, string $typ
|
|||||||
$googleCloudService = app(GoogleCloudService::class);
|
$googleCloudService = app(GoogleCloudService::class);
|
||||||
$content = $googleCloudService->downloadFromStorage($path);
|
$content = $googleCloudService->downloadFromStorage($path);
|
||||||
|
|
||||||
if (!$content) {
|
if (! $content) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '파일 다운로드에 실패했습니다.',
|
'message' => '파일 다운로드에 실패했습니다.',
|
||||||
@@ -248,7 +248,7 @@ public function downloadPhoto(Request $request, int $id, int $rowId, string $typ
|
|||||||
}
|
}
|
||||||
|
|
||||||
$extension = pathinfo($path, PATHINFO_EXTENSION) ?: 'jpg';
|
$extension = pathinfo($path, PATHINFO_EXTENSION) ?: 'jpg';
|
||||||
$mimeType = 'image/' . ($extension === 'jpg' ? 'jpeg' : $extension);
|
$mimeType = 'image/'.($extension === 'jpg' ? 'jpeg' : $extension);
|
||||||
|
|
||||||
$typeLabel = match ($type) {
|
$typeLabel = match ($type) {
|
||||||
'before' => '작업전',
|
'before' => '작업전',
|
||||||
@@ -273,7 +273,7 @@ public function addRow(int $id): JsonResponse
|
|||||||
{
|
{
|
||||||
$photo = ConstructionSitePhoto::find($id);
|
$photo = ConstructionSitePhoto::find($id);
|
||||||
|
|
||||||
if (!$photo) {
|
if (! $photo) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진대지를 찾을 수 없습니다.',
|
'message' => '사진대지를 찾을 수 없습니다.',
|
||||||
@@ -293,7 +293,7 @@ public function deleteRow(int $id, int $rowId): JsonResponse
|
|||||||
{
|
{
|
||||||
$photo = ConstructionSitePhoto::with('rows')->find($id);
|
$photo = ConstructionSitePhoto::with('rows')->find($id);
|
||||||
|
|
||||||
if (!$photo) {
|
if (! $photo) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진대지를 찾을 수 없습니다.',
|
'message' => '사진대지를 찾을 수 없습니다.',
|
||||||
@@ -309,7 +309,7 @@ public function deleteRow(int $id, int $rowId): JsonResponse
|
|||||||
|
|
||||||
$row = $photo->rows->firstWhere('id', $rowId);
|
$row = $photo->rows->firstWhere('id', $rowId);
|
||||||
|
|
||||||
if (!$row) {
|
if (! $row) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '사진 행을 찾을 수 없습니다.',
|
'message' => '사진 행을 찾을 수 없습니다.',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Juil;
|
namespace App\Http\Controllers\Juil;
|
||||||
|
|
||||||
use App\Helpers\AiTokenHelper;
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\Juil\MeetingMinute;
|
use App\Models\Juil\MeetingMinute;
|
||||||
use App\Services\GoogleCloudService;
|
use App\Services\GoogleCloudService;
|
||||||
@@ -172,7 +171,7 @@ public function saveSegments(Request $request, int $id): JsonResponse
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '세그먼트 저장 중 오류가 발생했습니다: ' . $e->getMessage(),
|
'message' => '세그먼트 저장 중 오류가 발생했습니다: '.$e->getMessage(),
|
||||||
], 500);
|
], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,7 +309,7 @@ public function downloadAudio(Request $request, int $id): Response|JsonResponse
|
|||||||
}
|
}
|
||||||
|
|
||||||
$extension = pathinfo($meeting->audio_file_path, PATHINFO_EXTENSION) ?: 'webm';
|
$extension = pathinfo($meeting->audio_file_path, PATHINFO_EXTENSION) ?: 'webm';
|
||||||
$mimeType = 'audio/' . $extension;
|
$mimeType = 'audio/'.$extension;
|
||||||
|
|
||||||
$safeTitle = preg_replace('/[\/\\\\:*?"<>|]/', '_', $meeting->title);
|
$safeTitle = preg_replace('/[\/\\\\:*?"<>|]/', '_', $meeting->title);
|
||||||
$filename = "{$safeTitle}.{$extension}";
|
$filename = "{$safeTitle}.{$extension}";
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ private function handlePresentationPage(Request $request, string $routeName): ?R
|
|||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
return response('', 200)->header('HX-Redirect', route($routeName));
|
return response('', 200)->header('HX-Redirect', route($routeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ public function labor(Request $request): View|Response
|
|||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
return response('', 200)->header('HX-Redirect', route('lab.strategy.labor'));
|
return response('', 200)->header('HX-Redirect', route('lab.strategy.labor'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('lab.strategy.labor');
|
return view('lab.strategy.labor');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@ public function chatbot(Request $request): View|Response
|
|||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
return response('', 200)->header('HX-Redirect', route('lab.strategy.chatbot'));
|
return response('', 200)->header('HX-Redirect', route('lab.strategy.chatbot'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('lab.strategy.chatbot');
|
return view('lab.strategy.chatbot');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +58,7 @@ public function knowledgeSearch(Request $request): View|Response
|
|||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
return response('', 200)->header('HX-Redirect', route('lab.strategy.knowledge-search'));
|
return response('', 200)->header('HX-Redirect', route('lab.strategy.knowledge-search'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('lab.strategy.knowledge-search');
|
return view('lab.strategy.knowledge-search');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +70,7 @@ public function chatbotCompare(Request $request): View|Response
|
|||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
return response('', 200)->header('HX-Redirect', route('lab.strategy.chatbot-compare'));
|
return response('', 200)->header('HX-Redirect', route('lab.strategy.chatbot-compare'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('lab.strategy.chatbot-compare');
|
return view('lab.strategy.chatbot-compare');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +82,7 @@ public function ragStartups(Request $request): View|Response
|
|||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
return response('', 200)->header('HX-Redirect', route('lab.strategy.rag-startups'));
|
return response('', 200)->header('HX-Redirect', route('lab.strategy.rag-startups'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('lab.strategy.rag-startups');
|
return view('lab.strategy.rag-startups');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +94,7 @@ public function douzone(Request $request): View|Response
|
|||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
return response('', 200)->header('HX-Redirect', route('lab.strategy.douzone'));
|
return response('', 200)->header('HX-Redirect', route('lab.strategy.douzone'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('lab.strategy.douzone');
|
return view('lab.strategy.douzone');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +106,7 @@ public function confluenceVsNotion(Request $request): View|Response
|
|||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
return response('', 200)->header('HX-Redirect', route('lab.strategy.confluence-vs-notion'));
|
return response('', 200)->header('HX-Redirect', route('lab.strategy.confluence-vs-notion'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('lab.strategy.confluence-vs-notion');
|
return view('lab.strategy.confluence-vs-notion');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +118,7 @@ public function salesStrategy(Request $request): View|Response
|
|||||||
if ($request->header('HX-Request')) {
|
if ($request->header('HX-Request')) {
|
||||||
return response('', 200)->header('HX-Redirect', route('lab.strategy.sales-strategy'));
|
return response('', 200)->header('HX-Redirect', route('lab.strategy.sales-strategy'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('lab.strategy.sales-strategy');
|
return view('lab.strategy.sales-strategy');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class MenuSyncController extends Controller
|
|||||||
protected function getTenantId(): int
|
protected function getTenantId(): int
|
||||||
{
|
{
|
||||||
$tenantId = session('selected_tenant_id');
|
$tenantId = session('selected_tenant_id');
|
||||||
|
|
||||||
return ($tenantId && $tenantId !== 'all') ? (int) $tenantId : 1;
|
return ($tenantId && $tenantId !== 'all') ? (int) $tenantId : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +230,7 @@ public function push(Request $request): JsonResponse
|
|||||||
$response = Http::withHeaders([
|
$response = Http::withHeaders([
|
||||||
'X-Menu-Sync-Key' => $env['api_key'],
|
'X-Menu-Sync-Key' => $env['api_key'],
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
])->post(rtrim($env['url'], '/') . '/menu-sync/import', [
|
])->post(rtrim($env['url'], '/').'/menu-sync/import', [
|
||||||
'menus' => $menuData,
|
'menus' => $menuData,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -244,7 +245,7 @@ public function push(Request $request): JsonResponse
|
|||||||
'error' => $response->json('error', '원격 서버 오류'),
|
'error' => $response->json('error', '원격 서버 오류'),
|
||||||
], $response->status());
|
], $response->status());
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return response()->json(['error' => '연결 실패: ' . $e->getMessage()], 500);
|
return response()->json(['error' => '연결 실패: '.$e->getMessage()], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,10 +308,11 @@ public function testConnection(Request $request): JsonResponse
|
|||||||
$response = Http::withHeaders([
|
$response = Http::withHeaders([
|
||||||
'X-Menu-Sync-Key' => $validated['api_key'],
|
'X-Menu-Sync-Key' => $validated['api_key'],
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
])->timeout(10)->get(rtrim($validated['url'], '/') . '/menu-sync/export');
|
])->timeout(10)->get(rtrim($validated['url'], '/').'/menu-sync/export');
|
||||||
|
|
||||||
if ($response->successful()) {
|
if ($response->successful()) {
|
||||||
$data = $response->json();
|
$data = $response->json();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => '연결 성공',
|
'message' => '연결 성공',
|
||||||
@@ -321,12 +323,12 @@ public function testConnection(Request $request): JsonResponse
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => 'API 오류: ' . $response->status(),
|
'message' => 'API 오류: '.$response->status(),
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '연결 실패: ' . $e->getMessage(),
|
'message' => '연결 실패: '.$e->getMessage(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -394,12 +396,12 @@ private function fetchRemoteMenus(array $env): array
|
|||||||
$response = Http::withHeaders([
|
$response = Http::withHeaders([
|
||||||
'X-Menu-Sync-Key' => $env['api_key'],
|
'X-Menu-Sync-Key' => $env['api_key'],
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
])->timeout(10)->get(rtrim($env['url'], '/') . '/menu-sync/export', [
|
])->timeout(10)->get(rtrim($env['url'], '/').'/menu-sync/export', [
|
||||||
'tenant_id' => $this->getTenantId(), // 현재 선택된 테넌트 전달
|
'tenant_id' => $this->getTenantId(), // 현재 선택된 테넌트 전달
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (! $response->successful()) {
|
if (! $response->successful()) {
|
||||||
throw new \Exception('API 오류: HTTP ' . $response->status());
|
throw new \Exception('API 오류: HTTP '.$response->status());
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $response->json();
|
$data = $response->json();
|
||||||
@@ -440,6 +442,7 @@ private function flattenMenuNames(array $menus, string $prefix = ''): array
|
|||||||
$names = array_merge($names, $this->flattenMenuNames($menu['children'], $fullName));
|
$names = array_merge($names, $this->flattenMenuNames($menu['children'], $fullName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $names;
|
return $names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class AdminProspectController extends Controller
|
|||||||
*/
|
*/
|
||||||
private function checkAdminAccess(): void
|
private function checkAdminAccess(): void
|
||||||
{
|
{
|
||||||
if (!auth()->user()->isAdmin() && !auth()->user()->isSuperAdmin()) {
|
if (! auth()->user()->isAdmin() && ! auth()->user()->isSuperAdmin()) {
|
||||||
abort(403, '관리자만 접근할 수 있습니다.');
|
abort(403, '관리자만 접근할 수 있습니다.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ private function getPaymentTypeForField(string $field): string
|
|||||||
*/
|
*/
|
||||||
private function loadMergedCommission(?SalesTenantManagement $management): ?object
|
private function loadMergedCommission(?SalesTenantManagement $management): ?object
|
||||||
{
|
{
|
||||||
if (!$management) {
|
if (! $management) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,12 +58,12 @@ private function loadMergedCommission(?SalesTenantManagement $management): ?obje
|
|||||||
$balance = $commissions->firstWhere('payment_type', SalesCommission::PAYMENT_BALANCE);
|
$balance = $commissions->firstWhere('payment_type', SalesCommission::PAYMENT_BALANCE);
|
||||||
|
|
||||||
// balance 레코드가 없으면 기존 단일 레코드 그대로 반환 (하위호환)
|
// balance 레코드가 없으면 기존 단일 레코드 그대로 반환 (하위호환)
|
||||||
if (!$balance) {
|
if (! $balance) {
|
||||||
return $deposit ?? $commissions->first();
|
return $deposit ?? $commissions->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1차 필드는 deposit, 2차 필드는 balance에서 가져옴
|
// 1차 필드는 deposit, 2차 필드는 balance에서 가져옴
|
||||||
$merged = new \stdClass();
|
$merged = new \stdClass;
|
||||||
$merged->first_payment_at = $deposit?->first_payment_at;
|
$merged->first_payment_at = $deposit?->first_payment_at;
|
||||||
$merged->first_partner_paid_at = $deposit?->first_partner_paid_at;
|
$merged->first_partner_paid_at = $deposit?->first_partner_paid_at;
|
||||||
$merged->second_payment_at = $balance->second_payment_at;
|
$merged->second_payment_at = $balance->second_payment_at;
|
||||||
@@ -118,7 +118,7 @@ public function modalShow(int $id): View
|
|||||||
|
|
||||||
// 파트너 타입
|
// 파트너 타입
|
||||||
$partnerType = $management->salesPartner?->partner_type;
|
$partnerType = $management->salesPartner?->partner_type;
|
||||||
if (!$partnerType && $prospect->registered_by) {
|
if (! $partnerType && $prospect->registered_by) {
|
||||||
$partnerType = SalesPartner::where('user_id', $prospect->registered_by)->value('partner_type');
|
$partnerType = SalesPartner::where('user_id', $prospect->registered_by)->value('partner_type');
|
||||||
}
|
}
|
||||||
$partnerType = $partnerType ?? 'individual';
|
$partnerType = $partnerType ?? 'individual';
|
||||||
@@ -161,7 +161,7 @@ private function getIndexData(Request $request): array
|
|||||||
$query = TenantProspect::with(['registeredBy', 'tenant']);
|
$query = TenantProspect::with(['registeredBy', 'tenant']);
|
||||||
|
|
||||||
// 검색
|
// 검색
|
||||||
if (!empty($filters['search'])) {
|
if (! empty($filters['search'])) {
|
||||||
$search = $filters['search'];
|
$search = $filters['search'];
|
||||||
$query->where(function ($q) use ($search) {
|
$query->where(function ($q) use ($search) {
|
||||||
$q->where('company_name', 'like', "%{$search}%")
|
$q->where('company_name', 'like', "%{$search}%")
|
||||||
@@ -174,7 +174,7 @@ private function getIndexData(Request $request): array
|
|||||||
// 상태 필터
|
// 상태 필터
|
||||||
$isProgressCompleteFilter = ($filters['status'] === 'progress_complete');
|
$isProgressCompleteFilter = ($filters['status'] === 'progress_complete');
|
||||||
$isHandoverFilter = ($filters['status'] === 'handover');
|
$isHandoverFilter = ($filters['status'] === 'handover');
|
||||||
if (!empty($filters['status']) && !$isProgressCompleteFilter && !$isHandoverFilter) {
|
if (! empty($filters['status']) && ! $isProgressCompleteFilter && ! $isHandoverFilter) {
|
||||||
$query->where('status', $filters['status']);
|
$query->where('status', $filters['status']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ private function getIndexData(Request $request): array
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 영업파트너 필터
|
// 영업파트너 필터
|
||||||
if (!empty($filters['registered_by'])) {
|
if (! empty($filters['registered_by'])) {
|
||||||
$query->where('registered_by', $filters['registered_by']);
|
$query->where('registered_by', $filters['registered_by']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ private function getIndexData(Request $request): array
|
|||||||
|
|
||||||
// 파트너 타입: management → registered_by 순으로 조회
|
// 파트너 타입: management → registered_by 순으로 조회
|
||||||
$partnerType = $management?->salesPartner?->partner_type;
|
$partnerType = $management?->salesPartner?->partner_type;
|
||||||
if (!$partnerType && $prospect->registered_by) {
|
if (! $partnerType && $prospect->registered_by) {
|
||||||
$partnerType = SalesPartner::where('user_id', $prospect->registered_by)->value('partner_type');
|
$partnerType = SalesPartner::where('user_id', $prospect->registered_by)->value('partner_type');
|
||||||
}
|
}
|
||||||
$prospect->partner_type = $partnerType ?? 'individual';
|
$prospect->partner_type = $partnerType ?? 'individual';
|
||||||
@@ -264,7 +264,7 @@ private function getIndexData(Request $request): array
|
|||||||
|
|
||||||
// 파트너 타입: management → registered_by 순으로 조회
|
// 파트너 타입: management → registered_by 순으로 조회
|
||||||
$partnerType = $management?->salesPartner?->partner_type;
|
$partnerType = $management?->salesPartner?->partner_type;
|
||||||
if (!$partnerType && $prospect->registered_by) {
|
if (! $partnerType && $prospect->registered_by) {
|
||||||
$partnerType = SalesPartner::where('user_id', $prospect->registered_by)->value('partner_type');
|
$partnerType = SalesPartner::where('user_id', $prospect->registered_by)->value('partner_type');
|
||||||
}
|
}
|
||||||
$prospect->partner_type = $partnerType ?? 'individual';
|
$prospect->partner_type = $partnerType ?? 'individual';
|
||||||
@@ -314,7 +314,7 @@ public function updateHqStatus(int $id, Request $request)
|
|||||||
$this->checkAdminAccess();
|
$this->checkAdminAccess();
|
||||||
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'hq_status' => 'required|in:' . implode(',', array_keys(SalesTenantManagement::$hqStatusLabels)),
|
'hq_status' => 'required|in:'.implode(',', array_keys(SalesTenantManagement::$hqStatusLabels)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$prospect = TenantProspect::findOrFail($id);
|
$prospect = TenantProspect::findOrFail($id);
|
||||||
@@ -410,7 +410,7 @@ public function updateCommissionDate(int $id, Request $request)
|
|||||||
|
|
||||||
// 파트너 resolve → 요율 결정
|
// 파트너 resolve → 요율 결정
|
||||||
$partner = $management->salesPartner;
|
$partner = $management->salesPartner;
|
||||||
if (!$partner && $prospect->registered_by) {
|
if (! $partner && $prospect->registered_by) {
|
||||||
$partner = SalesPartner::where('user_id', $prospect->registered_by)->first();
|
$partner = SalesPartner::where('user_id', $prospect->registered_by)->first();
|
||||||
}
|
}
|
||||||
$isGroup = $partner?->isGroup() ?? false;
|
$isGroup = $partner?->isGroup() ?? false;
|
||||||
@@ -557,7 +557,7 @@ public function updateReferrerCommission(int $id, Request $request)
|
|||||||
|
|
||||||
// 단체 파트너는 수동 수정 불가
|
// 단체 파트너는 수동 수정 불가
|
||||||
$partner = $management->salesPartner;
|
$partner = $management->salesPartner;
|
||||||
if (!$partner && $prospect->registered_by) {
|
if (! $partner && $prospect->registered_by) {
|
||||||
$partner = SalesPartner::where('user_id', $prospect->registered_by)->first();
|
$partner = SalesPartner::where('user_id', $prospect->registered_by)->first();
|
||||||
}
|
}
|
||||||
if ($partner && $partner->isGroup()) {
|
if ($partner && $partner->isGroup()) {
|
||||||
@@ -619,7 +619,7 @@ public function clearCommissionDate(int $id, Request $request)
|
|||||||
$prospect = TenantProspect::findOrFail($id);
|
$prospect = TenantProspect::findOrFail($id);
|
||||||
$management = SalesTenantManagement::where('tenant_prospect_id', $prospect->id)->first();
|
$management = SalesTenantManagement::where('tenant_prospect_id', $prospect->id)->first();
|
||||||
|
|
||||||
if (!$management) {
|
if (! $management) {
|
||||||
return response()->json(['success' => false, 'message' => '관리 정보가 없습니다.']);
|
return response()->json(['success' => false, 'message' => '관리 정보가 없습니다.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,7 +632,7 @@ public function clearCommissionDate(int $id, Request $request)
|
|||||||
->where('payment_type', $paymentType)
|
->where('payment_type', $paymentType)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if (!$commission) {
|
if (! $commission) {
|
||||||
return response()->json(['success' => false, 'message' => '수당 정보가 없습니다.']);
|
return response()->json(['success' => false, 'message' => '수당 정보가 없습니다.']);
|
||||||
}
|
}
|
||||||
$updateData = [$field => null];
|
$updateData = [$field => null];
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ public function uploadAudio(Request $request, GoogleCloudStorageService $gcs): J
|
|||||||
|
|
||||||
// 파일 저장
|
// 파일 저장
|
||||||
$file = $request->file('audio');
|
$file = $request->file('audio');
|
||||||
$fileName = 'audio_' . now()->format('Ymd_His') . '_' . uniqid() . '.' . $file->getClientOriginalExtension();
|
$fileName = 'audio_'.now()->format('Ymd_His').'_'.uniqid().'.'.$file->getClientOriginalExtension();
|
||||||
$localPath = $file->storeAs("tenant/consultations/{$tenantId}", $fileName, 'local');
|
$localPath = $file->storeAs("tenant/consultations/{$tenantId}", $fileName, 'local');
|
||||||
$fileSize = $file->getSize();
|
$fileSize = $file->getSize();
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ public function uploadAudio(Request $request, GoogleCloudStorageService $gcs): J
|
|||||||
'formatted_duration' => $consultation->formatted_duration,
|
'formatted_duration' => $consultation->formatted_duration,
|
||||||
'created_by_name' => $consultation->creator->name,
|
'created_by_name' => $consultation->creator->name,
|
||||||
'created_at' => $consultation->created_at->format('Y-m-d H:i'),
|
'created_at' => $consultation->created_at->format('Y-m-d H:i'),
|
||||||
'has_gcs' => !empty($gcsUri),
|
'has_gcs' => ! empty($gcsUri),
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ public function uploadFile(Request $request): JsonResponse
|
|||||||
// 파일 저장
|
// 파일 저장
|
||||||
$file = $request->file('file');
|
$file = $request->file('file');
|
||||||
$originalName = $file->getClientOriginalName();
|
$originalName = $file->getClientOriginalName();
|
||||||
$fileName = now()->format('Ymd_His') . '_' . uniqid() . '_' . $originalName;
|
$fileName = now()->format('Ymd_His').'_'.uniqid().'_'.$originalName;
|
||||||
$path = $file->storeAs("tenant/attachments/{$tenantId}", $fileName, 'local');
|
$path = $file->storeAs("tenant/attachments/{$tenantId}", $fileName, 'local');
|
||||||
|
|
||||||
// DB에 저장
|
// DB에 저장
|
||||||
@@ -229,7 +229,7 @@ public function downloadAudio(int $consultationId, GoogleCloudStorageService $gc
|
|||||||
|
|
||||||
// GCS에 저장된 경우 서명된 URL로 리다이렉트
|
// GCS에 저장된 경우 서명된 URL로 리다이렉트
|
||||||
if ($consultation->gcs_uri) {
|
if ($consultation->gcs_uri) {
|
||||||
$objectName = str_replace('gs://' . $gcs->getBucketName() . '/', '', $consultation->gcs_uri);
|
$objectName = str_replace('gs://'.$gcs->getBucketName().'/', '', $consultation->gcs_uri);
|
||||||
$signedUrl = $gcs->getSignedUrl($objectName, 60);
|
$signedUrl = $gcs->getSignedUrl($objectName, 60);
|
||||||
|
|
||||||
if ($signedUrl) {
|
if ($signedUrl) {
|
||||||
@@ -240,7 +240,7 @@ public function downloadAudio(int $consultationId, GoogleCloudStorageService $gc
|
|||||||
// 로컬 파일 다운로드
|
// 로컬 파일 다운로드
|
||||||
$localPath = Storage::disk('local')->path($consultation->file_path);
|
$localPath = Storage::disk('local')->path($consultation->file_path);
|
||||||
|
|
||||||
if (!file_exists($localPath)) {
|
if (! file_exists($localPath)) {
|
||||||
abort(404, '파일을 찾을 수 없습니다.');
|
abort(404, '파일을 찾을 수 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,11 +250,11 @@ public function downloadAudio(int $consultationId, GoogleCloudStorageService $gc
|
|||||||
'wav' => 'audio/wav',
|
'wav' => 'audio/wav',
|
||||||
'mp3' => 'audio/mpeg',
|
'mp3' => 'audio/mpeg',
|
||||||
'ogg' => 'audio/ogg',
|
'ogg' => 'audio/ogg',
|
||||||
'm4a' => 'audio/mp4'
|
'm4a' => 'audio/mp4',
|
||||||
];
|
];
|
||||||
$contentType = $mimeTypes[$extension] ?? 'audio/webm';
|
$contentType = $mimeTypes[$extension] ?? 'audio/webm';
|
||||||
|
|
||||||
$downloadFileName = '상담녹음_' . $consultation->created_at->format('Ymd_His') . '.' . $extension;
|
$downloadFileName = '상담녹음_'.$consultation->created_at->format('Ymd_His').'.'.$extension;
|
||||||
|
|
||||||
return response()->download($localPath, $downloadFileName, [
|
return response()->download($localPath, $downloadFileName, [
|
||||||
'Content-Type' => $contentType,
|
'Content-Type' => $contentType,
|
||||||
@@ -274,7 +274,7 @@ public function downloadFile(int $consultationId): BinaryFileResponse
|
|||||||
|
|
||||||
$localPath = Storage::disk('local')->path($consultation->file_path);
|
$localPath = Storage::disk('local')->path($consultation->file_path);
|
||||||
|
|
||||||
if (!file_exists($localPath)) {
|
if (! file_exists($localPath)) {
|
||||||
abort(404, '파일을 찾을 수 없습니다.');
|
abort(404, '파일을 찾을 수 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +359,7 @@ public function prospectUploadAudio(Request $request, GoogleCloudStorageService
|
|||||||
$duration = $request->input('duration');
|
$duration = $request->input('duration');
|
||||||
|
|
||||||
$file = $request->file('audio');
|
$file = $request->file('audio');
|
||||||
$fileName = 'audio_' . now()->format('Ymd_His') . '_' . uniqid() . '.' . $file->getClientOriginalExtension();
|
$fileName = 'audio_'.now()->format('Ymd_His').'_'.uniqid().'.'.$file->getClientOriginalExtension();
|
||||||
$localPath = $file->storeAs("prospect/consultations/{$prospectId}", $fileName, 'local');
|
$localPath = $file->storeAs("prospect/consultations/{$prospectId}", $fileName, 'local');
|
||||||
$fileSize = $file->getSize();
|
$fileSize = $file->getSize();
|
||||||
|
|
||||||
@@ -397,7 +397,7 @@ public function prospectUploadAudio(Request $request, GoogleCloudStorageService
|
|||||||
'formatted_duration' => $consultation->formatted_duration,
|
'formatted_duration' => $consultation->formatted_duration,
|
||||||
'created_by_name' => $consultation->creator->name,
|
'created_by_name' => $consultation->creator->name,
|
||||||
'created_at' => $consultation->created_at->format('Y-m-d H:i'),
|
'created_at' => $consultation->created_at->format('Y-m-d H:i'),
|
||||||
'has_gcs' => !empty($gcsUri),
|
'has_gcs' => ! empty($gcsUri),
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -420,7 +420,7 @@ public function prospectUploadFile(Request $request): JsonResponse
|
|||||||
|
|
||||||
$file = $request->file('file');
|
$file = $request->file('file');
|
||||||
$originalName = $file->getClientOriginalName();
|
$originalName = $file->getClientOriginalName();
|
||||||
$fileName = now()->format('Ymd_His') . '_' . uniqid() . '_' . $originalName;
|
$fileName = now()->format('Ymd_His').'_'.uniqid().'_'.$originalName;
|
||||||
$path = $file->storeAs("prospect/attachments/{$prospectId}", $fileName, 'local');
|
$path = $file->storeAs("prospect/attachments/{$prospectId}", $fileName, 'local');
|
||||||
|
|
||||||
$consultation = SalesConsultation::createFileByProspect(
|
$consultation = SalesConsultation::createFileByProspect(
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public function saveProducts(Request $request): JsonResponse
|
|||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '저장 중 오류가 발생했습니다: ' . $e->getMessage(),
|
'message' => '저장 중 오류가 발생했습니다: '.$e->getMessage(),
|
||||||
], 500);
|
], 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ private function getDashboardData(Request $request): array
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!$isGroupPartner) {
|
if (! $isGroupPartner) {
|
||||||
$commissionByRole[] = [
|
$commissionByRole[] = [
|
||||||
'name' => '관리자',
|
'name' => '관리자',
|
||||||
'rate' => null, // 1개월 구독료 (퍼센트가 아닌 고정 금액)
|
'rate' => null, // 1개월 구독료 (퍼센트가 아닌 고정 금액)
|
||||||
@@ -181,7 +181,7 @@ private function getDashboardData(Request $request): array
|
|||||||
|
|
||||||
// 수당 계산: 개발비 × 요율% (개인 20%, 단체 30%) - 1차/2차 분할은 calculateExpectedCommissionSummary에서 처리
|
// 수당 계산: 개발비 × 요율% (개인 20%, 단체 30%) - 1차/2차 분할은 calculateExpectedCommissionSummary에서 처리
|
||||||
$handoverPartnerRate = $isGroupPartner ? 0.30 : 0.20;
|
$handoverPartnerRate = $isGroupPartner ? 0.30 : 0.20;
|
||||||
$handoverPartnerCommission = (int)($handoverTotalRegFee * $handoverPartnerRate);
|
$handoverPartnerCommission = (int) ($handoverTotalRegFee * $handoverPartnerRate);
|
||||||
|
|
||||||
// 내가 매니저로 지정된 인계 완료 건의 수당 계산
|
// 내가 매니저로 지정된 인계 완료 건의 수당 계산
|
||||||
$managedHandoverManagements = SalesTenantManagement::where('manager_user_id', $currentUserId)
|
$managedHandoverManagements = SalesTenantManagement::where('manager_user_id', $currentUserId)
|
||||||
@@ -190,7 +190,7 @@ private function getDashboardData(Request $request): array
|
|||||||
$managedHandoverManagementIds = $managedHandoverManagements->pluck('id')->toArray();
|
$managedHandoverManagementIds = $managedHandoverManagements->pluck('id')->toArray();
|
||||||
|
|
||||||
// 매니저 수당: 1개월 구독료 (퍼센트가 아닌 고정 금액)
|
// 매니저 수당: 1개월 구독료 (퍼센트가 아닌 고정 금액)
|
||||||
$handoverManagerCommission = (int)SalesContractProduct::whereIn('management_id', $managedHandoverManagementIds)
|
$handoverManagerCommission = (int) SalesContractProduct::whereIn('management_id', $managedHandoverManagementIds)
|
||||||
->sum('subscription_fee');
|
->sum('subscription_fee');
|
||||||
|
|
||||||
// 기존 수당에 인계 완료 수당 추가
|
// 기존 수당에 인계 완료 수당 추가
|
||||||
@@ -202,7 +202,7 @@ private function getDashboardData(Request $request): array
|
|||||||
// 역할별 수당 업데이트 (실제 지급된 수당 기준)
|
// 역할별 수당 업데이트 (실제 지급된 수당 기준)
|
||||||
// 참고: 예상 수당은 나중에 $totalExpectedCommission으로 별도 계산됨
|
// 참고: 예상 수당은 나중에 $totalExpectedCommission으로 별도 계산됨
|
||||||
$commissionByRole[0]['amount'] = $partnerCommissionTotal;
|
$commissionByRole[0]['amount'] = $partnerCommissionTotal;
|
||||||
if (!$isGroupPartner) {
|
if (! $isGroupPartner) {
|
||||||
$commissionByRole[1]['amount'] = $managerCommissionTotal;
|
$commissionByRole[1]['amount'] = $managerCommissionTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ private function getDashboardData(Request $request): array
|
|||||||
->toArray();
|
->toArray();
|
||||||
$devInProgressRegFee = SalesContractProduct::whereIn('management_id', $devInProgressManagementIds)
|
$devInProgressRegFee = SalesContractProduct::whereIn('management_id', $devInProgressManagementIds)
|
||||||
->sum('registration_fee');
|
->sum('registration_fee');
|
||||||
$expectedFromDevInProgress = (int)($devInProgressRegFee * $handoverPartnerRate); // 개발비 × 요율 (개인 20%, 단체 30%)
|
$expectedFromDevInProgress = (int) ($devInProgressRegFee * $handoverPartnerRate); // 개발비 × 요율 (개인 20%, 단체 30%)
|
||||||
|
|
||||||
// 2) 인계 완료 중 지급 미완료 건
|
// 2) 인계 완료 중 지급 미완료 건
|
||||||
$handoverUnpaidRegFee = SalesContractProduct::whereIn('management_id', $handoverManagementIds)
|
$handoverUnpaidRegFee = SalesContractProduct::whereIn('management_id', $handoverManagementIds)
|
||||||
@@ -288,7 +288,7 @@ private function getDashboardData(Request $request): array
|
|||||||
->whereIn('management_id', $handoverManagementIds)
|
->whereIn('management_id', $handoverManagementIds)
|
||||||
->where('status', SalesCommission::STATUS_PAID)
|
->where('status', SalesCommission::STATUS_PAID)
|
||||||
->sum('partner_commission');
|
->sum('partner_commission');
|
||||||
$expectedFromHandover = (int)($handoverUnpaidRegFee * $handoverPartnerRate) - $paidCommissionFromHandover;
|
$expectedFromHandover = (int) ($handoverUnpaidRegFee * $handoverPartnerRate) - $paidCommissionFromHandover;
|
||||||
$expectedFromHandover = max(0, $expectedFromHandover);
|
$expectedFromHandover = max(0, $expectedFromHandover);
|
||||||
|
|
||||||
// 총 예상 수당 (지급 완료 제외)
|
// 총 예상 수당 (지급 완료 제외)
|
||||||
@@ -383,7 +383,7 @@ public function assignManager(int $tenantId, Request $request): JsonResponse
|
|||||||
} else {
|
} else {
|
||||||
// 특정 매니저 지정
|
// 특정 매니저 지정
|
||||||
$manager = User::find($managerId);
|
$manager = User::find($managerId);
|
||||||
if (!$manager) {
|
if (! $manager) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '매니저를 찾을 수 없습니다.',
|
'message' => '매니저를 찾을 수 없습니다.',
|
||||||
@@ -428,7 +428,7 @@ public function assignProspectManager(int $prospectId, Request $request): JsonRe
|
|||||||
} else {
|
} else {
|
||||||
// 특정 매니저 지정
|
// 특정 매니저 지정
|
||||||
$manager = User::find($managerId);
|
$manager = User::find($managerId);
|
||||||
if (!$manager) {
|
if (! $manager) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '매니저를 찾을 수 없습니다.',
|
'message' => '매니저를 찾을 수 없습니다.',
|
||||||
@@ -581,7 +581,7 @@ private function getManagerOnlyProspects(int $currentUserId): array
|
|||||||
$prospect = $management->tenantProspect;
|
$prospect = $management->tenantProspect;
|
||||||
|
|
||||||
// 내가 등록한 건은 제외 (순수하게 매니저로만 참여한 건만)
|
// 내가 등록한 건은 제외 (순수하게 매니저로만 참여한 건만)
|
||||||
if (!$prospect || $prospect->registered_by === $currentUserId) {
|
if (! $prospect || $prospect->registered_by === $currentUserId) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,7 +615,7 @@ private function getManagerOnlyProspects(int $currentUserId): array
|
|||||||
|
|
||||||
foreach ($tenantManagements as $management) {
|
foreach ($tenantManagements as $management) {
|
||||||
$tenant = $management->tenant;
|
$tenant = $management->tenant;
|
||||||
if (!$tenant) {
|
if (! $tenant) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,12 +676,12 @@ private function calculatePartnerSummaryStats(array $partnerIds, int $currentUse
|
|||||||
// 개인 파트너 예상 수당: 1개월 구독료
|
// 개인 파트너 예상 수당: 1개월 구독료
|
||||||
$individualProspectIds = TenantProspect::whereIn('registered_by', $individualPartnerIds)->pluck('id')->toArray();
|
$individualProspectIds = TenantProspect::whereIn('registered_by', $individualPartnerIds)->pluck('id')->toArray();
|
||||||
$individualMgmtIds = SalesTenantManagement::whereIn('tenant_prospect_id', $individualProspectIds)->pluck('id')->toArray();
|
$individualMgmtIds = SalesTenantManagement::whereIn('tenant_prospect_id', $individualProspectIds)->pluck('id')->toArray();
|
||||||
$individualExpected = (int)SalesContractProduct::whereIn('management_id', $individualMgmtIds)->sum('subscription_fee');
|
$individualExpected = (int) SalesContractProduct::whereIn('management_id', $individualMgmtIds)->sum('subscription_fee');
|
||||||
|
|
||||||
// 단체 파트너 예상 수당: 개발비 × 3%
|
// 단체 파트너 예상 수당: 개발비 × 3%
|
||||||
$groupProspectIds = TenantProspect::whereIn('registered_by', $groupPartnerUserIds)->pluck('id')->toArray();
|
$groupProspectIds = TenantProspect::whereIn('registered_by', $groupPartnerUserIds)->pluck('id')->toArray();
|
||||||
$groupMgmtIds = SalesTenantManagement::whereIn('tenant_prospect_id', $groupProspectIds)->pluck('id')->toArray();
|
$groupMgmtIds = SalesTenantManagement::whereIn('tenant_prospect_id', $groupProspectIds)->pluck('id')->toArray();
|
||||||
$groupExpected = (int)(SalesContractProduct::whereIn('management_id', $groupMgmtIds)->sum('registration_fee') * 0.03);
|
$groupExpected = (int) (SalesContractProduct::whereIn('management_id', $groupMgmtIds)->sum('registration_fee') * 0.03);
|
||||||
|
|
||||||
$expectedFromFee = $individualExpected + $groupExpected;
|
$expectedFromFee = $individualExpected + $groupExpected;
|
||||||
|
|
||||||
@@ -727,16 +727,16 @@ private function calculatePartnerSummaryStats(array $partnerIds, int $currentUse
|
|||||||
'expected_commission' => $expectedCommission,
|
'expected_commission' => $expectedCommission,
|
||||||
'paid_commission' => $paidManagerCommission,
|
'paid_commission' => $paidManagerCommission,
|
||||||
'first_commission' => [
|
'first_commission' => [
|
||||||
'total' => (int)$halfExpected,
|
'total' => (int) $halfExpected,
|
||||||
'pending' => (int)$halfPending,
|
'pending' => (int) $halfPending,
|
||||||
'scheduled' => (int)$halfScheduled,
|
'scheduled' => (int) $halfScheduled,
|
||||||
'paid' => (int)$halfPaid,
|
'paid' => (int) $halfPaid,
|
||||||
],
|
],
|
||||||
'second_commission' => [
|
'second_commission' => [
|
||||||
'total' => (int)$halfExpected,
|
'total' => (int) $halfExpected,
|
||||||
'pending' => (int)$halfPending,
|
'pending' => (int) $halfPending,
|
||||||
'scheduled' => (int)$halfScheduled,
|
'scheduled' => (int) $halfScheduled,
|
||||||
'paid' => (int)$halfPaid,
|
'paid' => (int) $halfPaid,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -782,8 +782,8 @@ private function getPartnerActivitiesDetail($recruitedPartners, int $currentUser
|
|||||||
// 단체 파트너: 협업지원금(개발비 × 3%), 개인 파트너: 관리자 수당(1개월 구독료)
|
// 단체 파트너: 협업지원금(개발비 × 3%), 개인 파트너: 관리자 수당(1개월 구독료)
|
||||||
$isRecruitedGroupPartner = $salesPartner && $salesPartner->isGroup();
|
$isRecruitedGroupPartner = $salesPartner && $salesPartner->isGroup();
|
||||||
$expectedCommission = $isRecruitedGroupPartner
|
$expectedCommission = $isRecruitedGroupPartner
|
||||||
? (int)($totalRegistrationFee * 0.03)
|
? (int) ($totalRegistrationFee * 0.03)
|
||||||
: (int)$totalSubscriptionFee;
|
: (int) $totalSubscriptionFee;
|
||||||
|
|
||||||
// 최종 수당 (확정 + 예상 중 큰 값)
|
// 최종 수당 (확정 + 예상 중 큰 값)
|
||||||
$managerCommission = max($confirmedCommission, $expectedCommission);
|
$managerCommission = max($confirmedCommission, $expectedCommission);
|
||||||
@@ -864,7 +864,7 @@ private function getPartnerActivitiesDetail($recruitedPartners, int $currentUser
|
|||||||
|
|
||||||
// 역할 정보
|
// 역할 정보
|
||||||
$roles = $partner->userRoles->pluck('role.name')->filter()->toArray();
|
$roles = $partner->userRoles->pluck('role.name')->filter()->toArray();
|
||||||
$roleLabel = !empty($roles) ? implode(', ', $roles) : '영업';
|
$roleLabel = ! empty($roles) ? implode(', ', $roles) : '영업';
|
||||||
|
|
||||||
$activities[] = [
|
$activities[] = [
|
||||||
'partner' => $partner,
|
'partner' => $partner,
|
||||||
@@ -912,7 +912,7 @@ private function getCommissionData(): array
|
|||||||
*/
|
*/
|
||||||
private function getAllManagerUsers()
|
private function getAllManagerUsers()
|
||||||
{
|
{
|
||||||
return User::whereHas('userRoles.role', fn($q) => $q->where('name', 'manager'))
|
return User::whereHas('userRoles.role', fn ($q) => $q->where('name', 'manager'))
|
||||||
->where('is_active', true)
|
->where('is_active', true)
|
||||||
->where('id', '!=', auth()->id()) // 본인 제외
|
->where('id', '!=', auth()->id()) // 본인 제외
|
||||||
->get(['id', 'name', 'email']);
|
->get(['id', 'name', 'email']);
|
||||||
@@ -930,7 +930,7 @@ public function searchManagers(Request $request): JsonResponse
|
|||||||
// 디버깅: SQL 쿼리 로깅
|
// 디버깅: SQL 쿼리 로깅
|
||||||
\DB::enableQueryLog();
|
\DB::enableQueryLog();
|
||||||
|
|
||||||
$managers = User::whereHas('userRoles.role', fn($q) => $q->where('name', 'manager'))
|
$managers = User::whereHas('userRoles.role', fn ($q) => $q->where('name', 'manager'))
|
||||||
->where('is_active', true)
|
->where('is_active', true)
|
||||||
->where('id', '!=', $authId)
|
->where('id', '!=', $authId)
|
||||||
->when($query, function ($q) use ($query) {
|
->when($query, function ($q) use ($query) {
|
||||||
@@ -1012,24 +1012,24 @@ private function calculateExpectedCommissionSummary($commissions, int $totalExpe
|
|||||||
return [
|
return [
|
||||||
'scheduled_this_month' => $commissions
|
'scheduled_this_month' => $commissions
|
||||||
->where('status', SalesCommission::STATUS_APPROVED)
|
->where('status', SalesCommission::STATUS_APPROVED)
|
||||||
->filter(fn($c) => $c->scheduled_payment_date?->format('Y-m') === $thisMonth)
|
->filter(fn ($c) => $c->scheduled_payment_date?->format('Y-m') === $thisMonth)
|
||||||
->sum('partner_commission'),
|
->sum('partner_commission'),
|
||||||
'total_received' => $paidCommission,
|
'total_received' => $paidCommission,
|
||||||
'pending_amount' => $pendingAmount,
|
'pending_amount' => $pendingAmount,
|
||||||
'contracts_this_month' => $commissions
|
'contracts_this_month' => $commissions
|
||||||
->filter(fn($c) => $c->payment_date >= $thisMonthStart && $c->payment_date <= $thisMonthEnd)
|
->filter(fn ($c) => $c->payment_date >= $thisMonthStart && $c->payment_date <= $thisMonthEnd)
|
||||||
->count(),
|
->count(),
|
||||||
'first_commission' => [
|
'first_commission' => [
|
||||||
'total' => (int)$halfExpected,
|
'total' => (int) $halfExpected,
|
||||||
'pending' => (int)$halfPending,
|
'pending' => (int) $halfPending,
|
||||||
'scheduled' => (int)$halfScheduled,
|
'scheduled' => (int) $halfScheduled,
|
||||||
'paid' => (int)$halfPaid,
|
'paid' => (int) $halfPaid,
|
||||||
],
|
],
|
||||||
'second_commission' => [
|
'second_commission' => [
|
||||||
'total' => (int)$halfExpected,
|
'total' => (int) $halfExpected,
|
||||||
'pending' => (int)$halfPending,
|
'pending' => (int) $halfPending,
|
||||||
'scheduled' => (int)$halfScheduled,
|
'scheduled' => (int) $halfScheduled,
|
||||||
'paid' => (int)$halfPaid,
|
'paid' => (int) $halfPaid,
|
||||||
],
|
],
|
||||||
'total_commission' => $totalExpectedCommission,
|
'total_commission' => $totalExpectedCommission,
|
||||||
];
|
];
|
||||||
@@ -1053,7 +1053,7 @@ private function calculateCommissionSummaryFromCollection($commissions): array
|
|||||||
return [
|
return [
|
||||||
'scheduled_this_month' => $commissions
|
'scheduled_this_month' => $commissions
|
||||||
->where('status', SalesCommission::STATUS_APPROVED)
|
->where('status', SalesCommission::STATUS_APPROVED)
|
||||||
->filter(fn($c) => $c->scheduled_payment_date?->format('Y-m') === $thisMonth)
|
->filter(fn ($c) => $c->scheduled_payment_date?->format('Y-m') === $thisMonth)
|
||||||
->sum('partner_commission'),
|
->sum('partner_commission'),
|
||||||
'total_received' => $commissions
|
'total_received' => $commissions
|
||||||
->where('status', SalesCommission::STATUS_PAID)
|
->where('status', SalesCommission::STATUS_PAID)
|
||||||
@@ -1062,7 +1062,7 @@ private function calculateCommissionSummaryFromCollection($commissions): array
|
|||||||
->where('status', SalesCommission::STATUS_PENDING)
|
->where('status', SalesCommission::STATUS_PENDING)
|
||||||
->sum('partner_commission'),
|
->sum('partner_commission'),
|
||||||
'contracts_this_month' => $commissions
|
'contracts_this_month' => $commissions
|
||||||
->filter(fn($c) => $c->payment_date >= $thisMonthStart && $c->payment_date <= $thisMonthEnd)
|
->filter(fn ($c) => $c->payment_date >= $thisMonthStart && $c->payment_date <= $thisMonthEnd)
|
||||||
->count(),
|
->count(),
|
||||||
'first_commission' => $firstCommission,
|
'first_commission' => $firstCommission,
|
||||||
'second_commission' => $secondCommission,
|
'second_commission' => $secondCommission,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public function __construct(
|
|||||||
public function index(Request $request): View|Response
|
public function index(Request $request): View|Response
|
||||||
{
|
{
|
||||||
// 권한 체크: admin 역할만 접근 가능
|
// 권한 체크: admin 역할만 접근 가능
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '접근 권한이 없습니다.');
|
abort(403, '접근 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ public function index(Request $request): View|Response
|
|||||||
public function approve(Request $request, int $id)
|
public function approve(Request $request, int $id)
|
||||||
{
|
{
|
||||||
// 권한 체크
|
// 권한 체크
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '접근 권한이 없습니다.');
|
abort(403, '접근 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ public function approve(Request $request, int $id)
|
|||||||
public function reject(Request $request, int $id)
|
public function reject(Request $request, int $id)
|
||||||
{
|
{
|
||||||
// 권한 체크
|
// 권한 체크
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '접근 권한이 없습니다.');
|
abort(403, '접근 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ public function reject(Request $request, int $id)
|
|||||||
public function updateStatus(Request $request, int $id)
|
public function updateStatus(Request $request, int $id)
|
||||||
{
|
{
|
||||||
// 권한 체크
|
// 권한 체크
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '접근 권한이 없습니다.');
|
abort(403, '접근 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ public function updateStatus(Request $request, int $id)
|
|||||||
public function revertToPending(Request $request, int $id)
|
public function revertToPending(Request $request, int $id)
|
||||||
{
|
{
|
||||||
// 권한 체크
|
// 권한 체크
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '접근 권한이 없습니다.');
|
abort(403, '접근 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ public function revertToPending(Request $request, int $id)
|
|||||||
public function detail(int $id): View
|
public function detail(int $id): View
|
||||||
{
|
{
|
||||||
// 권한 체크
|
// 권한 체크
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '접근 권한이 없습니다.');
|
abort(403, '접근 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ public function update(Request $request, int $id)
|
|||||||
|
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'name' => 'required|string|max:100',
|
'name' => 'required|string|max:100',
|
||||||
'email' => 'required|email|max:255|unique:users,email,' . $id,
|
'email' => 'required|email|max:255|unique:users,email,'.$id,
|
||||||
'phone' => 'nullable|string|max:20',
|
'phone' => 'nullable|string|max:20',
|
||||||
'password' => 'nullable|string|min:4|confirmed',
|
'password' => 'nullable|string|min:4|confirmed',
|
||||||
'role_ids' => 'required|array|min:1',
|
'role_ids' => 'required|array|min:1',
|
||||||
@@ -242,7 +242,7 @@ public function update(Request $request, int $id)
|
|||||||
public function destroy(int $id)
|
public function destroy(int $id)
|
||||||
{
|
{
|
||||||
// 권한 체크: admin 역할만 삭제 가능
|
// 권한 체크: admin 역할만 삭제 가능
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '삭제 권한이 없습니다.');
|
abort(403, '삭제 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,6 +298,7 @@ public function delegateRole(Request $request, int $id)
|
|||||||
$this->service->delegateRole($fromUser, $toUser, $validated['role_name']);
|
$this->service->delegateRole($fromUser, $toUser, $validated['role_name']);
|
||||||
|
|
||||||
$roleLabel = '상담매니저';
|
$roleLabel = '상담매니저';
|
||||||
|
|
||||||
return redirect()->back()
|
return redirect()->back()
|
||||||
->with('success', "{$roleLabel} 역할이 {$toUser->name}님에게 위임되었습니다.");
|
->with('success', "{$roleLabel} 역할이 {$toUser->name}님에게 위임되었습니다.");
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
@@ -319,6 +320,7 @@ public function assignRole(Request $request, int $id)
|
|||||||
$this->service->assignRole($partner, $validated['role_name']);
|
$this->service->assignRole($partner, $validated['role_name']);
|
||||||
|
|
||||||
$roleLabels = ['sales' => '영업파트너', 'manager' => '상담매니저'];
|
$roleLabels = ['sales' => '영업파트너', 'manager' => '상담매니저'];
|
||||||
|
|
||||||
return redirect()->back()
|
return redirect()->back()
|
||||||
->with('success', "{$roleLabels[$validated['role_name']]} 역할이 부여되었습니다.");
|
->with('success', "{$roleLabels[$validated['role_name']]} 역할이 부여되었습니다.");
|
||||||
}
|
}
|
||||||
@@ -336,6 +338,7 @@ public function removeRole(Request $request, int $id)
|
|||||||
$this->service->removeRole($partner, $validated['role_name']);
|
$this->service->removeRole($partner, $validated['role_name']);
|
||||||
|
|
||||||
$roleLabels = ['sales' => '영업파트너', 'manager' => '상담매니저'];
|
$roleLabels = ['sales' => '영업파트너', 'manager' => '상담매니저'];
|
||||||
|
|
||||||
return redirect()->back()
|
return redirect()->back()
|
||||||
->with('success', "{$roleLabels[$validated['role_name']]} 역할이 제거되었습니다.");
|
->with('success', "{$roleLabels[$validated['role_name']]} 역할이 제거되었습니다.");
|
||||||
}
|
}
|
||||||
@@ -371,7 +374,7 @@ public function deleteDocument(int $id, int $documentId)
|
|||||||
public function approvals(Request $request): View|Response
|
public function approvals(Request $request): View|Response
|
||||||
{
|
{
|
||||||
// 권한 체크: admin 역할만 접근 가능
|
// 권한 체크: admin 역할만 접근 가능
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '접근 권한이 없습니다.');
|
abort(403, '접근 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,7 +407,7 @@ public function approvals(Request $request): View|Response
|
|||||||
public function approveFromList(Request $request, int $id)
|
public function approveFromList(Request $request, int $id)
|
||||||
{
|
{
|
||||||
// 권한 체크
|
// 권한 체크
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '접근 권한이 없습니다.');
|
abort(403, '접근 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,7 +431,7 @@ public function approveFromList(Request $request, int $id)
|
|||||||
public function rejectFromList(Request $request, int $id)
|
public function rejectFromList(Request $request, int $id)
|
||||||
{
|
{
|
||||||
// 권한 체크
|
// 권한 체크
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
abort(403, '접근 권한이 없습니다.');
|
abort(403, '접근 권한이 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public function index(Request $request): View|Response
|
|||||||
|
|
||||||
$categories = SalesProductCategory::active()
|
$categories = SalesProductCategory::active()
|
||||||
->ordered()
|
->ordered()
|
||||||
->with(['products' => fn($q) => $q->ordered()])
|
->with(['products' => fn ($q) => $q->ordered()])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$currentCategoryCode = $request->input('category', $categories->first()?->code);
|
$currentCategoryCode = $request->input('category', $categories->first()?->code);
|
||||||
@@ -43,7 +43,7 @@ public function productList(Request $request): View
|
|||||||
{
|
{
|
||||||
$categoryCode = $request->input('category');
|
$categoryCode = $request->input('category');
|
||||||
$category = SalesProductCategory::where('code', $categoryCode)
|
$category = SalesProductCategory::where('code', $categoryCode)
|
||||||
->with(['products' => fn($q) => $q->ordered()])
|
->with(['products' => fn ($q) => $q->ordered()])
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
return view('sales.products.partials.product-list', compact('category'));
|
return view('sales.products.partials.product-list', compact('category'));
|
||||||
@@ -144,7 +144,7 @@ public function destroy(int $id): JsonResponse
|
|||||||
public function toggleActive(int $id): JsonResponse
|
public function toggleActive(int $id): JsonResponse
|
||||||
{
|
{
|
||||||
$product = SalesProduct::findOrFail($id);
|
$product = SalesProduct::findOrFail($id);
|
||||||
$product->update(['is_active' => !$product->is_active]);
|
$product->update(['is_active' => ! $product->is_active]);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
@@ -268,7 +268,7 @@ public function getProductsApi(): JsonResponse
|
|||||||
{
|
{
|
||||||
$categories = SalesProductCategory::active()
|
$categories = SalesProductCategory::active()
|
||||||
->ordered()
|
->ordered()
|
||||||
->with(['products' => fn($q) => $q->active()->ordered()])
|
->with(['products' => fn ($q) => $q->active()->ordered()])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
|
|||||||
@@ -99,19 +99,19 @@ public function store(Request $request)
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// 명함 이미지 저장 (Base64)
|
// 명함 이미지 저장 (Base64)
|
||||||
if (!empty($validated['business_card_image_data'])) {
|
if (! empty($validated['business_card_image_data'])) {
|
||||||
$validated['business_card_image'] = $this->saveBase64Image($validated['business_card_image_data'], 'business-cards');
|
$validated['business_card_image'] = $this->saveBase64Image($validated['business_card_image_data'], 'business-cards');
|
||||||
}
|
}
|
||||||
unset($validated['business_card_image_data']);
|
unset($validated['business_card_image_data']);
|
||||||
|
|
||||||
// 신분증 이미지 저장 (Base64)
|
// 신분증 이미지 저장 (Base64)
|
||||||
if (!empty($validated['id_card_image_data'])) {
|
if (! empty($validated['id_card_image_data'])) {
|
||||||
$validated['id_card_image'] = $this->saveBase64Image($validated['id_card_image_data'], 'id-cards');
|
$validated['id_card_image'] = $this->saveBase64Image($validated['id_card_image_data'], 'id-cards');
|
||||||
}
|
}
|
||||||
unset($validated['id_card_image_data']);
|
unset($validated['id_card_image_data']);
|
||||||
|
|
||||||
// 통장사본 이미지 저장 (Base64)
|
// 통장사본 이미지 저장 (Base64)
|
||||||
if (!empty($validated['bankbook_image_data'])) {
|
if (! empty($validated['bankbook_image_data'])) {
|
||||||
$validated['bankbook_image'] = $this->saveBase64Image($validated['bankbook_image_data'], 'bankbooks');
|
$validated['bankbook_image'] = $this->saveBase64Image($validated['bankbook_image_data'], 'bankbooks');
|
||||||
}
|
}
|
||||||
unset($validated['bankbook_image_data']);
|
unset($validated['bankbook_image_data']);
|
||||||
@@ -132,7 +132,7 @@ public function show(int $id): View
|
|||||||
'salesManager',
|
'salesManager',
|
||||||
'products',
|
'products',
|
||||||
'scenarios',
|
'scenarios',
|
||||||
'consultations.manager'
|
'consultations.manager',
|
||||||
])->findOrFail($id);
|
])->findOrFail($id);
|
||||||
|
|
||||||
$managers = SalesManager::active()
|
$managers = SalesManager::active()
|
||||||
@@ -250,7 +250,7 @@ private function saveBase64Image(string $base64Data, string $folder): ?string
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$filename = $folder . '/' . date('Ymd') . '_' . uniqid() . '.' . $extension;
|
$filename = $folder.'/'.date('Ymd').'_'.uniqid().'.'.$extension;
|
||||||
Storage::disk('public')->put($filename, $imageData);
|
Storage::disk('public')->put($filename, $imageData);
|
||||||
|
|
||||||
return $filename;
|
return $filename;
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
use App\Models\Sales\SalesTenantManagement;
|
use App\Models\Sales\SalesTenantManagement;
|
||||||
use App\Models\Sales\TenantProspect;
|
use App\Models\Sales\TenantProspect;
|
||||||
use App\Models\Tenants\Tenant;
|
use App\Models\Tenants\Tenant;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public function store(Request $request)
|
|||||||
|
|
||||||
// 등록 가능 여부 확인
|
// 등록 가능 여부 확인
|
||||||
$checkResult = $this->service->canRegister($validated['business_number']);
|
$checkResult = $this->service->canRegister($validated['business_number']);
|
||||||
if (!$checkResult['can_register']) {
|
if (! $checkResult['can_register']) {
|
||||||
return redirect()->back()
|
return redirect()->back()
|
||||||
->withInput()
|
->withInput()
|
||||||
->with('error', $checkResult['reason']);
|
->with('error', $checkResult['reason']);
|
||||||
@@ -172,7 +172,7 @@ public function destroy(int $id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 관리자만 삭제 가능
|
// 관리자만 삭제 가능
|
||||||
if (!auth()->user()->isAdmin()) {
|
if (! auth()->user()->isAdmin()) {
|
||||||
return redirect()->route('sales.prospects.index')
|
return redirect()->route('sales.prospects.index')
|
||||||
->with('error', '삭제 권한이 없습니다. 본사 운영팀에 문의하세요.');
|
->with('error', '삭제 권한이 없습니다. 본사 운영팀에 문의하세요.');
|
||||||
}
|
}
|
||||||
@@ -257,7 +257,7 @@ public function deleteAttachment(Request $request, int $id)
|
|||||||
$type = $request->get('type');
|
$type = $request->get('type');
|
||||||
$allowedTypes = ['business_card', 'id_card', 'bankbook'];
|
$allowedTypes = ['business_card', 'id_card', 'bankbook'];
|
||||||
|
|
||||||
if (!in_array($type, $allowedTypes)) {
|
if (! in_array($type, $allowedTypes)) {
|
||||||
return response()->json(['success' => false, 'message' => '잘못된 요청입니다.'], 400);
|
return response()->json(['success' => false, 'message' => '잘못된 요청입니다.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
use App\Services\GoogleCloudService;
|
use App\Services\GoogleCloudService;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ public function bulkStore(Request $request): JsonResponse
|
|||||||
|
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$skipped++;
|
$skipped++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ public function index(Request $request): View
|
|||||||
|
|
||||||
// 날짜 범위 필터
|
// 날짜 범위 필터
|
||||||
if ($request->filled('date_from')) {
|
if ($request->filled('date_from')) {
|
||||||
$query->where('created_at', '>=', $request->date_from . ' 00:00:00');
|
$query->where('created_at', '>=', $request->date_from.' 00:00:00');
|
||||||
}
|
}
|
||||||
if ($request->filled('date_to')) {
|
if ($request->filled('date_to')) {
|
||||||
$query->where('created_at', '<=', $request->date_to . ' 23:59:59');
|
$query->where('created_at', '<=', $request->date_to.' 23:59:59');
|
||||||
}
|
}
|
||||||
|
|
||||||
$alerts = $query->paginate(50)->withQueryString();
|
$alerts = $query->paginate(50)->withQueryString();
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public function upload(Request $request): JsonResponse
|
|||||||
'screenshots.*' => 'required|image|max:10240', // 최대 10MB
|
'screenshots.*' => 'required|image|max:10240', // 최대 10MB
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$uploadDir = storage_path('app/tutorial_uploads/' . auth()->id() . '/' . time());
|
$uploadDir = storage_path('app/tutorial_uploads/'.auth()->id().'/'.time());
|
||||||
if (! is_dir($uploadDir)) {
|
if (! is_dir($uploadDir)) {
|
||||||
mkdir($uploadDir, 0755, true);
|
mkdir($uploadDir, 0755, true);
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ public function upload(Request $request): JsonResponse
|
|||||||
foreach ($request->file('screenshots') as $i => $file) {
|
foreach ($request->file('screenshots') as $i => $file) {
|
||||||
$filename = sprintf('screenshot_%02d.%s', $i + 1, $file->getClientOriginalExtension());
|
$filename = sprintf('screenshot_%02d.%s', $i + 1, $file->getClientOriginalExtension());
|
||||||
$file->move($uploadDir, $filename);
|
$file->move($uploadDir, $filename);
|
||||||
$paths[] = $uploadDir . '/' . $filename;
|
$paths[] = $uploadDir.'/'.$filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@@ -79,7 +79,7 @@ public function analyze(Request $request): JsonResponse
|
|||||||
if (! file_exists($path)) {
|
if (! file_exists($path)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => '업로드된 파일을 찾을 수 없습니다: ' . basename($path),
|
'message' => '업로드된 파일을 찾을 수 없습니다: '.basename($path),
|
||||||
], 400);
|
], 400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ public function analyze(Request $request): JsonResponse
|
|||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
'message' => 'AI 분석 중 오류가 발생했습니다: ' . $e->getMessage(),
|
'message' => 'AI 분석 중 오류가 발생했습니다: '.$e->getMessage(),
|
||||||
], 500);
|
], 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ public function download(int $id): BinaryFileResponse|RedirectResponse|JsonRespo
|
|||||||
return response()->json(['message' => '영상 파일을 찾을 수 없습니다.'], 404);
|
return response()->json(['message' => '영상 파일을 찾을 수 없습니다.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$filename = 'tutorial_' . ($tutorial->title ? preg_replace('/[^a-zA-Z0-9가-힣_\-.]/', '_', $tutorial->title) : $tutorial->id) . '.mp4';
|
$filename = 'tutorial_'.($tutorial->title ? preg_replace('/[^a-zA-Z0-9가-힣_\-.]/', '_', $tutorial->title) : $tutorial->id).'.mp4';
|
||||||
|
|
||||||
return response()->download($tutorial->output_path, $filename, [
|
return response()->download($tutorial->output_path, $filename, [
|
||||||
'Content-Type' => 'video/mp4',
|
'Content-Type' => 'video/mp4',
|
||||||
|
|||||||
@@ -355,12 +355,12 @@ private function buildYoutubeText(VideoGeneration $video, array $scenario, array
|
|||||||
// 해시태그 생성
|
// 해시태그 생성
|
||||||
$hashtags = ['#shorts', '#쇼츠'];
|
$hashtags = ['#shorts', '#쇼츠'];
|
||||||
if ($keyword) {
|
if ($keyword) {
|
||||||
$hashtags[] = '#' . str_replace(' ', '', $keyword);
|
$hashtags[] = '#'.str_replace(' ', '', $keyword);
|
||||||
}
|
}
|
||||||
// 시나리오에서 추가 태그 추출
|
// 시나리오에서 추가 태그 추출
|
||||||
$bgmMood = $scenario['bgm_mood'] ?? '';
|
$bgmMood = $scenario['bgm_mood'] ?? '';
|
||||||
if ($bgmMood) {
|
if ($bgmMood) {
|
||||||
$hashtags[] = '#' . $bgmMood;
|
$hashtags[] = '#'.$bgmMood;
|
||||||
}
|
}
|
||||||
$hashtags = array_merge($hashtags, ['#건강', '#건강정보', '#헬스']);
|
$hashtags = array_merge($hashtags, ['#건강', '#건강정보', '#헬스']);
|
||||||
|
|
||||||
@@ -373,7 +373,7 @@ private function buildYoutubeText(VideoGeneration $video, array $scenario, array
|
|||||||
foreach ($scenes as $scene) {
|
foreach ($scenes as $scene) {
|
||||||
$narration = $scene['narration'] ?? '';
|
$narration = $scene['narration'] ?? '';
|
||||||
if ($narration && ($scene['scene_type'] ?? '') !== 'HOOK') {
|
if ($narration && ($scene['scene_type'] ?? '') !== 'HOOK') {
|
||||||
$descLines[] = '- ' . mb_substr($narration, 0, 60);
|
$descLines[] = '- '.mb_substr($narration, 0, 60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,16 +30,18 @@ public function handle(Request $request, Closure $next): Response
|
|||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
|
||||||
// HQ 테넌트 소속 확인
|
// HQ 테넌트 소속 확인
|
||||||
if (!$user->belongsToHQ()) {
|
if (! $user->belongsToHQ()) {
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
Log::info('[AutoLoginViaRemember] Non-HQ user rejected', ['user_id' => $user->id]);
|
Log::info('[AutoLoginViaRemember] Non-HQ user rejected', ['user_id' => $user->id]);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 활성 상태 확인
|
// 활성 상태 확인
|
||||||
if (!$user->is_active) {
|
if (! $user->is_active) {
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
Log::info('[AutoLoginViaRemember] Inactive user rejected', ['user_id' => $user->id]);
|
Log::info('[AutoLoginViaRemember] Inactive user rejected', ['user_id' => $user->id]);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\VideoGeneration;
|
use App\Models\VideoGeneration;
|
||||||
|
use App\Services\GoogleCloudStorageService;
|
||||||
use App\Services\Video\BgmService;
|
use App\Services\Video\BgmService;
|
||||||
use App\Services\Video\GeminiScriptService;
|
use App\Services\Video\GeminiScriptService;
|
||||||
use App\Services\Video\TtsService;
|
use App\Services\Video\TtsService;
|
||||||
use App\Services\Video\VeoVideoService;
|
use App\Services\Video\VeoVideoService;
|
||||||
use App\Services\Video\VideoAssemblyService;
|
use App\Services\Video\VideoAssemblyService;
|
||||||
use App\Services\GoogleCloudStorageService;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
@@ -114,7 +114,7 @@ public function handle(
|
|||||||
$video->updateProgress(
|
$video->updateProgress(
|
||||||
VideoGeneration::STATUS_GENERATING_CLIPS,
|
VideoGeneration::STATUS_GENERATING_CLIPS,
|
||||||
20 + (int) (($sceneNum / count($scenes)) * 10),
|
20 + (int) (($sceneNum / count($scenes)) * 10),
|
||||||
"영상 클립 생성 요청 중 ({$sceneNum}/" . count($scenes) . ')'
|
"영상 클립 생성 요청 중 ({$sceneNum}/".count($scenes).')'
|
||||||
);
|
);
|
||||||
|
|
||||||
$result = $veo->generateClip($prompt, $duration);
|
$result = $veo->generateClip($prompt, $duration);
|
||||||
@@ -137,7 +137,7 @@ public function handle(
|
|||||||
$video->updateProgress(
|
$video->updateProgress(
|
||||||
VideoGeneration::STATUS_GENERATING_CLIPS,
|
VideoGeneration::STATUS_GENERATING_CLIPS,
|
||||||
30 + (int) (($sceneNum / count($scenes)) * 40),
|
30 + (int) (($sceneNum / count($scenes)) * 40),
|
||||||
"영상 클립 생성 대기 중 ({$sceneNum}/" . count($scenes) . ')'
|
"영상 클립 생성 대기 중 ({$sceneNum}/".count($scenes).')'
|
||||||
);
|
);
|
||||||
|
|
||||||
$result = $veo->waitAndSave(
|
$result = $veo->waitAndSave(
|
||||||
@@ -189,7 +189,7 @@ public function handle(
|
|||||||
|
|
||||||
// 성공한 클립이 절반 미만이면 전체 실패
|
// 성공한 클립이 절반 미만이면 전체 실패
|
||||||
if (count($clipPaths) < ceil(count($scenes) / 2)) {
|
if (count($clipPaths) < ceil(count($scenes) / 2)) {
|
||||||
$video->markFailed('영상 클립 생성 실패 (성공: ' . count($clipPaths) . '/' . count($scenes) . ')');
|
$video->markFailed('영상 클립 생성 실패 (성공: '.count($clipPaths).'/'.count($scenes).')');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -310,7 +310,7 @@ public function handle(
|
|||||||
// === 완료 ===
|
// === 완료 ===
|
||||||
$stepMsg = empty($skippedScenes)
|
$stepMsg = empty($skippedScenes)
|
||||||
? '완료'
|
? '완료'
|
||||||
: '완료 (장면 ' . implode(',', $skippedScenes) . ' 건너뜀)';
|
: '완료 (장면 '.implode(',', $skippedScenes).' 건너뜀)';
|
||||||
|
|
||||||
$video->update([
|
$video->update([
|
||||||
'status' => VideoGeneration::STATUS_COMPLETED,
|
'status' => VideoGeneration::STATUS_COMPLETED,
|
||||||
@@ -344,7 +344,7 @@ public function handle(
|
|||||||
public function failed(\Throwable $exception): void
|
public function failed(\Throwable $exception): void
|
||||||
{
|
{
|
||||||
$video = VideoGeneration::withoutGlobalScopes()->find($this->videoGenerationId);
|
$video = VideoGeneration::withoutGlobalScopes()->find($this->videoGenerationId);
|
||||||
$video?->markFailed('Job 실패: ' . $exception->getMessage());
|
$video?->markFailed('Job 실패: '.$exception->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -356,6 +356,6 @@ private function makeSafePrompt(string $prompt): string
|
|||||||
$prompt = preg_replace('/\b(woman|man|girl|boy|person|people|her|his|she|he)\b/i', 'subject', $prompt);
|
$prompt = preg_replace('/\b(woman|man|girl|boy|person|people|her|his|she|he)\b/i', 'subject', $prompt);
|
||||||
|
|
||||||
// 안전 키워드 추가
|
// 안전 키워드 추가
|
||||||
return 'Safe for all audiences. Professional stock footage style. ' . $prompt;
|
return 'Safe for all audiences. Professional stock footage style. '.$prompt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
use App\Models\ESign\EsignSigner;
|
use App\Models\ESign\EsignSigner;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Mail\Mailables\Content;
|
|
||||||
use Illuminate\Mail\Mailables\Address;
|
use Illuminate\Mail\Mailables\Address;
|
||||||
|
use Illuminate\Mail\Mailables\Content;
|
||||||
use Illuminate\Mail\Mailables\Envelope;
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -32,7 +32,7 @@ public function envelope(): Envelope
|
|||||||
|
|
||||||
public function content(): Content
|
public function content(): Content
|
||||||
{
|
{
|
||||||
$downloadUrl = config('app.url') . '/esign/sign/' . $this->signer->access_token . '/api/document';
|
$downloadUrl = config('app.url').'/esign/sign/'.$this->signer->access_token.'/api/document';
|
||||||
|
|
||||||
return new Content(
|
return new Content(
|
||||||
html: 'emails.esign.completed',
|
html: 'emails.esign.completed',
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Mail\Mailables\Content;
|
|
||||||
use Illuminate\Mail\Mailables\Address;
|
use Illuminate\Mail\Mailables\Address;
|
||||||
|
use Illuminate\Mail\Mailables\Content;
|
||||||
use Illuminate\Mail\Mailables\Envelope;
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
use App\Models\ESign\EsignSigner;
|
use App\Models\ESign\EsignSigner;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Mail\Mailables\Content;
|
|
||||||
use Illuminate\Mail\Mailables\Address;
|
use Illuminate\Mail\Mailables\Address;
|
||||||
|
use Illuminate\Mail\Mailables\Content;
|
||||||
use Illuminate\Mail\Mailables\Envelope;
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ public function envelope(): Envelope
|
|||||||
|
|
||||||
public function content(): Content
|
public function content(): Content
|
||||||
{
|
{
|
||||||
$signUrl = config('app.url') . '/esign/sign/' . $this->signer->access_token;
|
$signUrl = config('app.url').'/esign/sign/'.$this->signer->access_token;
|
||||||
|
|
||||||
return new Content(
|
return new Content(
|
||||||
html: 'emails.esign.request',
|
html: 'emails.esign.request',
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Models\Barobill;
|
namespace App\Models\Barobill;
|
||||||
|
|
||||||
|
use App\Models\Tenants\Tenant;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use App\Models\Tenants\Tenant;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 계정과목 모델
|
* 계정과목 모델
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Models\Barobill;
|
namespace App\Models\Barobill;
|
||||||
|
|
||||||
|
use App\Models\Tenants\Tenant;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use App\Models\Tenants\Tenant;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 바로빌 계좌 거래 동기화 상태 모델
|
* 바로빌 계좌 거래 동기화 상태 모델
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Models\Barobill;
|
namespace App\Models\Barobill;
|
||||||
|
|
||||||
|
use App\Models\Tenants\Tenant;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use App\Models\Tenants\Tenant;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 계좌 입출금 거래 분개 모델
|
* 계좌 입출금 거래 분개 모델
|
||||||
@@ -62,7 +62,7 @@ public static function getByDateRange(int $tenantId, string $startDate, string $
|
|||||||
$grouped = [];
|
$grouped = [];
|
||||||
foreach ($splits as $split) {
|
foreach ($splits as $split) {
|
||||||
$key = $split->original_unique_key;
|
$key = $split->original_unique_key;
|
||||||
if (!isset($grouped[$key])) {
|
if (! isset($grouped[$key])) {
|
||||||
$grouped[$key] = [];
|
$grouped[$key] = [];
|
||||||
}
|
}
|
||||||
$grouped[$key][] = $split;
|
$grouped[$key][] = $split;
|
||||||
|
|||||||
@@ -102,6 +102,6 @@ public function getMaskedCertKeyAttribute(): string
|
|||||||
return $this->cert_key;
|
return $this->cert_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
return substr($this->cert_key, 0, 8) . str_repeat('*', 8) . '...';
|
return substr($this->cert_key, 0, 8).str_repeat('*', 8).'...';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,9 @@ public function getFormattedBizNoAttribute(): string
|
|||||||
{
|
{
|
||||||
$bizNo = preg_replace('/[^0-9]/', '', $this->biz_no);
|
$bizNo = preg_replace('/[^0-9]/', '', $this->biz_no);
|
||||||
if (strlen($bizNo) === 10) {
|
if (strlen($bizNo) === 10) {
|
||||||
return substr($bizNo, 0, 3) . '-' . substr($bizNo, 3, 2) . '-' . substr($bizNo, 5);
|
return substr($bizNo, 0, 3).'-'.substr($bizNo, 3, 2).'-'.substr($bizNo, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->biz_no;
|
return $this->biz_no;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ class BarobillPricingPolicy extends Model
|
|||||||
* 서비스 유형 상수
|
* 서비스 유형 상수
|
||||||
*/
|
*/
|
||||||
public const TYPE_CARD = 'card';
|
public const TYPE_CARD = 'card';
|
||||||
|
|
||||||
public const TYPE_TAX_INVOICE = 'tax_invoice';
|
public const TYPE_TAX_INVOICE = 'tax_invoice';
|
||||||
|
|
||||||
public const TYPE_BANK_ACCOUNT = 'bank_account';
|
public const TYPE_BANK_ACCOUNT = 'bank_account';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -60,10 +60,19 @@ public function tenant(): BelongsTo
|
|||||||
public function getActiveServicesAttribute(): array
|
public function getActiveServicesAttribute(): array
|
||||||
{
|
{
|
||||||
$services = [];
|
$services = [];
|
||||||
if ($this->use_tax_invoice) $services[] = 'tax_invoice';
|
if ($this->use_tax_invoice) {
|
||||||
if ($this->use_bank_account) $services[] = 'bank_account';
|
$services[] = 'tax_invoice';
|
||||||
if ($this->use_card_usage) $services[] = 'card_usage';
|
}
|
||||||
if ($this->use_hometax) $services[] = 'hometax';
|
if ($this->use_bank_account) {
|
||||||
|
$services[] = 'bank_account';
|
||||||
|
}
|
||||||
|
if ($this->use_card_usage) {
|
||||||
|
$services[] = 'card_usage';
|
||||||
|
}
|
||||||
|
if ($this->use_hometax) {
|
||||||
|
$services[] = 'hometax';
|
||||||
|
}
|
||||||
|
|
||||||
return $services;
|
return $services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,12 +74,13 @@ public function getServiceTypeLabelAttribute(): string
|
|||||||
*/
|
*/
|
||||||
public function getStatusLabelAttribute(): string
|
public function getStatusLabelAttribute(): string
|
||||||
{
|
{
|
||||||
if (!$this->is_active) {
|
if (! $this->is_active) {
|
||||||
return '비활성';
|
return '비활성';
|
||||||
}
|
}
|
||||||
if ($this->ended_at && $this->ended_at->isPast()) {
|
if ($this->ended_at && $this->ended_at->isPast()) {
|
||||||
return '종료';
|
return '종료';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '구독중';
|
return '구독중';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,12 +89,13 @@ public function getStatusLabelAttribute(): string
|
|||||||
*/
|
*/
|
||||||
public function getStatusColorAttribute(): string
|
public function getStatusColorAttribute(): string
|
||||||
{
|
{
|
||||||
if (!$this->is_active) {
|
if (! $this->is_active) {
|
||||||
return 'bg-gray-100 text-gray-800';
|
return 'bg-gray-100 text-gray-800';
|
||||||
}
|
}
|
||||||
if ($this->ended_at && $this->ended_at->isPast()) {
|
if ($this->ended_at && $this->ended_at->isPast()) {
|
||||||
return 'bg-red-100 text-red-800';
|
return 'bg-red-100 text-red-800';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'bg-green-100 text-green-800';
|
return 'bg-green-100 text-green-800';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Models\Barobill;
|
namespace App\Models\Barobill;
|
||||||
|
|
||||||
|
use App\Models\Tenants\Tenant;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use App\Models\Tenants\Tenant;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 바로빌 카드 사용내역 모델
|
* 바로빌 카드 사용내역 모델
|
||||||
@@ -103,6 +103,6 @@ public static function getByDateRange(int $tenantId, string $startDate, string $
|
|||||||
$query->where('card_num', $cardNum);
|
$query->where('card_num', $cardNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query->get()->keyBy(fn($item) => $item->unique_key);
|
return $query->get()->keyBy(fn ($item) => $item->unique_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Models\Barobill;
|
namespace App\Models\Barobill;
|
||||||
|
|
||||||
|
use App\Models\Tenants\Tenant;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use App\Models\Tenants\Tenant;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 카드 거래 분개 모델
|
* 카드 거래 분개 모델
|
||||||
@@ -67,7 +67,7 @@ public static function getByDateRange(int $tenantId, string $startDate, string $
|
|||||||
$grouped = [];
|
$grouped = [];
|
||||||
foreach ($splits as $split) {
|
foreach ($splits as $split) {
|
||||||
$key = $split->original_unique_key;
|
$key = $split->original_unique_key;
|
||||||
if (!isset($grouped[$key])) {
|
if (! isset($grouped[$key])) {
|
||||||
$grouped[$key] = [];
|
$grouped[$key] = [];
|
||||||
}
|
}
|
||||||
$grouped[$key][] = $split;
|
$grouped[$key][] = $split;
|
||||||
|
|||||||
@@ -99,15 +99,19 @@ class HometaxInvoice extends Model
|
|||||||
|
|
||||||
// 과세유형 상수
|
// 과세유형 상수
|
||||||
public const TAX_TYPE_TAXABLE = 1; // 과세
|
public const TAX_TYPE_TAXABLE = 1; // 과세
|
||||||
|
|
||||||
public const TAX_TYPE_ZERO_RATE = 2; // 영세
|
public const TAX_TYPE_ZERO_RATE = 2; // 영세
|
||||||
|
|
||||||
public const TAX_TYPE_EXEMPT = 3; // 면세
|
public const TAX_TYPE_EXEMPT = 3; // 면세
|
||||||
|
|
||||||
// 영수/청구 상수
|
// 영수/청구 상수
|
||||||
public const PURPOSE_TYPE_RECEIPT = 1; // 영수
|
public const PURPOSE_TYPE_RECEIPT = 1; // 영수
|
||||||
|
|
||||||
public const PURPOSE_TYPE_CLAIM = 2; // 청구
|
public const PURPOSE_TYPE_CLAIM = 2; // 청구
|
||||||
|
|
||||||
// 발급유형 상수
|
// 발급유형 상수
|
||||||
public const ISSUE_TYPE_NORMAL = 1; // 정발행
|
public const ISSUE_TYPE_NORMAL = 1; // 정발행
|
||||||
|
|
||||||
public const ISSUE_TYPE_REVERSE = 2; // 역발행
|
public const ISSUE_TYPE_REVERSE = 2; // 역발행
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -147,7 +151,7 @@ public function scopePurchase($query)
|
|||||||
*/
|
*/
|
||||||
public function scopePeriod($query, string $startDate, string $endDate, string $dateType = 'write')
|
public function scopePeriod($query, string $startDate, string $endDate, string $dateType = 'write')
|
||||||
{
|
{
|
||||||
$column = match($dateType) {
|
$column = match ($dateType) {
|
||||||
'issue' => 'issue_date',
|
'issue' => 'issue_date',
|
||||||
'send' => 'send_date',
|
'send' => 'send_date',
|
||||||
default => 'write_date',
|
default => 'write_date',
|
||||||
@@ -184,7 +188,7 @@ public function scopeSearchCorp($query, string $keyword, string $invoiceType = '
|
|||||||
*/
|
*/
|
||||||
public function getTaxTypeNameAttribute(): string
|
public function getTaxTypeNameAttribute(): string
|
||||||
{
|
{
|
||||||
return match($this->tax_type) {
|
return match ($this->tax_type) {
|
||||||
self::TAX_TYPE_TAXABLE => '과세',
|
self::TAX_TYPE_TAXABLE => '과세',
|
||||||
self::TAX_TYPE_ZERO_RATE => '영세',
|
self::TAX_TYPE_ZERO_RATE => '영세',
|
||||||
self::TAX_TYPE_EXEMPT => '면세',
|
self::TAX_TYPE_EXEMPT => '면세',
|
||||||
@@ -197,7 +201,7 @@ public function getTaxTypeNameAttribute(): string
|
|||||||
*/
|
*/
|
||||||
public function getPurposeTypeNameAttribute(): string
|
public function getPurposeTypeNameAttribute(): string
|
||||||
{
|
{
|
||||||
return match($this->purpose_type) {
|
return match ($this->purpose_type) {
|
||||||
self::PURPOSE_TYPE_RECEIPT => '영수',
|
self::PURPOSE_TYPE_RECEIPT => '영수',
|
||||||
self::PURPOSE_TYPE_CLAIM => '청구',
|
self::PURPOSE_TYPE_CLAIM => '청구',
|
||||||
default => '-',
|
default => '-',
|
||||||
@@ -209,7 +213,7 @@ public function getPurposeTypeNameAttribute(): string
|
|||||||
*/
|
*/
|
||||||
public function getIssueTypeNameAttribute(): string
|
public function getIssueTypeNameAttribute(): string
|
||||||
{
|
{
|
||||||
return match($this->issue_type) {
|
return match ($this->issue_type) {
|
||||||
self::ISSUE_TYPE_NORMAL => '정발급',
|
self::ISSUE_TYPE_NORMAL => '정발급',
|
||||||
self::ISSUE_TYPE_REVERSE => '역발급',
|
self::ISSUE_TYPE_REVERSE => '역발급',
|
||||||
default => '-',
|
default => '-',
|
||||||
@@ -247,17 +251,17 @@ public static function fromApiData(array $apiData, int $tenantId, string $invoic
|
|||||||
{
|
{
|
||||||
// 작성일자 파싱
|
// 작성일자 파싱
|
||||||
$writeDate = null;
|
$writeDate = null;
|
||||||
if (!empty($apiData['writeDate']) && strlen($apiData['writeDate']) >= 8) {
|
if (! empty($apiData['writeDate']) && strlen($apiData['writeDate']) >= 8) {
|
||||||
$writeDate = substr($apiData['writeDate'], 0, 4) . '-' .
|
$writeDate = substr($apiData['writeDate'], 0, 4).'-'.
|
||||||
substr($apiData['writeDate'], 4, 2) . '-' .
|
substr($apiData['writeDate'], 4, 2).'-'.
|
||||||
substr($apiData['writeDate'], 6, 2);
|
substr($apiData['writeDate'], 6, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 발급일자 파싱
|
// 발급일자 파싱
|
||||||
$issueDate = null;
|
$issueDate = null;
|
||||||
if (!empty($apiData['issueDT']) && strlen($apiData['issueDT']) >= 8) {
|
if (! empty($apiData['issueDT']) && strlen($apiData['issueDT']) >= 8) {
|
||||||
$issueDate = substr($apiData['issueDT'], 0, 4) . '-' .
|
$issueDate = substr($apiData['issueDT'], 0, 4).'-'.
|
||||||
substr($apiData['issueDT'], 4, 2) . '-' .
|
substr($apiData['issueDT'], 4, 2).'-'.
|
||||||
substr($apiData['issueDT'], 6, 2);
|
substr($apiData['issueDT'], 6, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,11 +277,11 @@ public static function fromApiData(array $apiData, int $tenantId, string $invoic
|
|||||||
'invoicee_corp_num' => $apiData['invoiceeCorpNum'] ?? '',
|
'invoicee_corp_num' => $apiData['invoiceeCorpNum'] ?? '',
|
||||||
'invoicee_corp_name' => $apiData['invoiceeCorpName'] ?? '',
|
'invoicee_corp_name' => $apiData['invoiceeCorpName'] ?? '',
|
||||||
'invoicee_ceo_name' => $apiData['invoiceeCEOName'] ?? null,
|
'invoicee_ceo_name' => $apiData['invoiceeCEOName'] ?? null,
|
||||||
'supply_amount' => (int)($apiData['supplyAmount'] ?? 0),
|
'supply_amount' => (int) ($apiData['supplyAmount'] ?? 0),
|
||||||
'tax_amount' => (int)($apiData['taxAmount'] ?? 0),
|
'tax_amount' => (int) ($apiData['taxAmount'] ?? 0),
|
||||||
'total_amount' => (int)($apiData['totalAmount'] ?? 0),
|
'total_amount' => (int) ($apiData['totalAmount'] ?? 0),
|
||||||
'tax_type' => (int)($apiData['taxType'] ?? 1),
|
'tax_type' => (int) ($apiData['taxType'] ?? 1),
|
||||||
'purpose_type' => (int)($apiData['purposeType'] ?? 1),
|
'purpose_type' => (int) ($apiData['purposeType'] ?? 1),
|
||||||
'issue_type' => 1, // 기본값: 정발행
|
'issue_type' => 1, // 기본값: 정발행
|
||||||
'item_name' => $apiData['itemName'] ?? null,
|
'item_name' => $apiData['itemName'] ?? null,
|
||||||
'remark' => $apiData['remark'] ?? null,
|
'remark' => $apiData['remark'] ?? null,
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Models\Barobill;
|
namespace App\Models\Barobill;
|
||||||
|
|
||||||
|
use App\Models\Tenants\Tenant;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use App\Models\Tenants\Tenant;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 홈택스 세금계산서 분개 모델
|
* 홈택스 세금계산서 분개 모델
|
||||||
@@ -72,15 +72,15 @@ public static function saveJournals(int $tenantId, int $invoiceId, array $invoic
|
|||||||
'dc_type' => $line['dc_type'],
|
'dc_type' => $line['dc_type'],
|
||||||
'account_code' => $line['account_code'],
|
'account_code' => $line['account_code'],
|
||||||
'account_name' => $line['account_name'],
|
'account_name' => $line['account_name'],
|
||||||
'debit_amount' => (int)($line['debit_amount'] ?? 0),
|
'debit_amount' => (int) ($line['debit_amount'] ?? 0),
|
||||||
'credit_amount' => (int)($line['credit_amount'] ?? 0),
|
'credit_amount' => (int) ($line['credit_amount'] ?? 0),
|
||||||
'description' => $line['description'] ?? '',
|
'description' => $line['description'] ?? '',
|
||||||
'sort_order' => $index,
|
'sort_order' => $index,
|
||||||
'invoice_type' => $invoiceData['invoice_type'] ?? '',
|
'invoice_type' => $invoiceData['invoice_type'] ?? '',
|
||||||
'write_date' => $invoiceData['write_date'] ?? null,
|
'write_date' => $invoiceData['write_date'] ?? null,
|
||||||
'supply_amount' => (int)($invoiceData['supply_amount'] ?? 0),
|
'supply_amount' => (int) ($invoiceData['supply_amount'] ?? 0),
|
||||||
'tax_amount' => (int)($invoiceData['tax_amount'] ?? 0),
|
'tax_amount' => (int) ($invoiceData['tax_amount'] ?? 0),
|
||||||
'total_amount' => (int)($invoiceData['total_amount'] ?? 0),
|
'total_amount' => (int) ($invoiceData['total_amount'] ?? 0),
|
||||||
'trading_partner_name' => $invoiceData['trading_partner_name'] ?? '',
|
'trading_partner_name' => $invoiceData['trading_partner_name'] ?? '',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,6 @@ public function getMaskedApiKeyAttribute(): string
|
|||||||
return $this->api_key;
|
return $this->api_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
return substr($this->api_key, 0, 8) . str_repeat('*', 8) . '...';
|
return substr($this->api_key, 0, 8).str_repeat('*', 8).'...';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ protected static function boot()
|
|||||||
*/
|
*/
|
||||||
public static function generateInquiryKey(): string
|
public static function generateInquiryKey(): string
|
||||||
{
|
{
|
||||||
return date('Ymd') . Str::upper(Str::random(24));
|
return date('Ymd').Str::upper(Str::random(24));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,8 +126,9 @@ public function getFormattedCompanyKeyAttribute(): string
|
|||||||
{
|
{
|
||||||
$key = $this->company_key;
|
$key = $this->company_key;
|
||||||
if (strlen($key) === 10) {
|
if (strlen($key) === 10) {
|
||||||
return substr($key, 0, 3) . '-' . substr($key, 3, 2) . '-' . substr($key, 5);
|
return substr($key, 0, 3).'-'.substr($key, 3, 2).'-'.substr($key, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $key;
|
return $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,10 +200,10 @@ public static function createFromApiResponse(
|
|||||||
$businessType = $companyInfoData['bizcnd'] ?? $companyInfoData['indutyNm'] ?? null;
|
$businessType = $companyInfoData['bizcnd'] ?? $companyInfoData['indutyNm'] ?? null;
|
||||||
$businessItem = $companyInfoData['bizitm'] ?? $companyInfoData['indutyDetailNm'] ?? null;
|
$businessItem = $companyInfoData['bizitm'] ?? $companyInfoData['indutyDetailNm'] ?? null;
|
||||||
$establishmentDate = null;
|
$establishmentDate = null;
|
||||||
if (!empty($companyInfoData['estbDt']) || !empty($companyInfoData['estbDate'])) {
|
if (! empty($companyInfoData['estbDt']) || ! empty($companyInfoData['estbDate'])) {
|
||||||
$estbDt = $companyInfoData['estbDt'] ?? $companyInfoData['estbDate'];
|
$estbDt = $companyInfoData['estbDt'] ?? $companyInfoData['estbDate'];
|
||||||
if (strlen($estbDt) === 8) {
|
if (strlen($estbDt) === 8) {
|
||||||
$establishmentDate = substr($estbDt, 0, 4) . '-' . substr($estbDt, 4, 2) . '-' . substr($estbDt, 6, 2);
|
$establishmentDate = substr($estbDt, 0, 4).'-'.substr($estbDt, 4, 2).'-'.substr($estbDt, 6, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,8 +217,8 @@ public static function createFromApiResponse(
|
|||||||
$ntsStatus = $ntsData['b_stt'] ?? null;
|
$ntsStatus = $ntsData['b_stt'] ?? null;
|
||||||
$ntsStatusCode = $ntsData['b_stt_cd'] ?? null;
|
$ntsStatusCode = $ntsData['b_stt_cd'] ?? null;
|
||||||
$ntsTaxType = $ntsData['tax_type'] ?? null;
|
$ntsTaxType = $ntsData['tax_type'] ?? null;
|
||||||
if (!empty($ntsData['end_dt']) && strlen($ntsData['end_dt']) === 8) {
|
if (! empty($ntsData['end_dt']) && strlen($ntsData['end_dt']) === 8) {
|
||||||
$ntsClosureDate = substr($ntsData['end_dt'], 0, 4) . '-' . substr($ntsData['end_dt'], 4, 2) . '-' . substr($ntsData['end_dt'], 6, 2);
|
$ntsClosureDate = substr($ntsData['end_dt'], 0, 4).'-'.substr($ntsData['end_dt'], 4, 2).'-'.substr($ntsData['end_dt'], 6, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +231,7 @@ public static function createFromApiResponse(
|
|||||||
if (isset($apiResult[$key]['success']) && $apiResult[$key]['success']) {
|
if (isset($apiResult[$key]['success']) && $apiResult[$key]['success']) {
|
||||||
$successCount++;
|
$successCount++;
|
||||||
} else {
|
} else {
|
||||||
$errors[] = $key . ': ' . ($apiResult[$key]['error'] ?? 'Unknown error');
|
$errors[] = $key.': '.($apiResult[$key]['error'] ?? 'Unknown error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
class DocumentTemplate extends Model
|
class DocumentTemplate extends Model
|
||||||
{
|
{
|
||||||
use HasFactory, BelongsToTenant, SoftDeletes;
|
use BelongsToTenant, HasFactory, SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'tenant_id',
|
'tenant_id',
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models\ESign;
|
namespace App\Models\ESign;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
namespace App\Models\ESign;
|
namespace App\Models\ESign;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
class EsignFieldTemplate extends Model
|
class EsignFieldTemplate extends Model
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -86,14 +86,14 @@ public function getFormattedBalanceAttribute(): string
|
|||||||
$amount = abs($this->balance);
|
$amount = abs($this->balance);
|
||||||
|
|
||||||
if ($amount >= 100000000) {
|
if ($amount >= 100000000) {
|
||||||
return number_format($amount / 100000000, 1) . '억원';
|
return number_format($amount / 100000000, 1).'억원';
|
||||||
} elseif ($amount >= 10000000) {
|
} elseif ($amount >= 10000000) {
|
||||||
return number_format($amount / 10000000, 0) . '천만원';
|
return number_format($amount / 10000000, 0).'천만원';
|
||||||
} elseif ($amount >= 10000) {
|
} elseif ($amount >= 10000) {
|
||||||
return number_format($amount / 10000, 0) . '만원';
|
return number_format($amount / 10000, 0).'만원';
|
||||||
}
|
}
|
||||||
|
|
||||||
return number_format($amount) . '원';
|
return number_format($amount).'원';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,7 +106,7 @@ public function getMaskedAccountNumberAttribute(): string
|
|||||||
return $number;
|
return $number;
|
||||||
}
|
}
|
||||||
|
|
||||||
return substr($number, 0, 3) . '-***-' . substr($number, -4);
|
return substr($number, 0, 3).'-***-'.substr($number, -4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ class BankTransaction extends Model
|
|||||||
|
|
||||||
// 거래 유형 상수
|
// 거래 유형 상수
|
||||||
public const TYPE_DEPOSIT = 'deposit'; // 입금
|
public const TYPE_DEPOSIT = 'deposit'; // 입금
|
||||||
|
|
||||||
public const TYPE_WITHDRAWAL = 'withdrawal'; // 출금
|
public const TYPE_WITHDRAWAL = 'withdrawal'; // 출금
|
||||||
|
|
||||||
public const TYPE_TRANSFER = 'transfer'; // 이체
|
public const TYPE_TRANSFER = 'transfer'; // 이체
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
@@ -106,7 +108,8 @@ public function getIsWithdrawalAttribute(): bool
|
|||||||
public function getFormattedAmountAttribute(): string
|
public function getFormattedAmountAttribute(): string
|
||||||
{
|
{
|
||||||
$prefix = $this->is_deposit ? '+' : '-';
|
$prefix = $this->is_deposit ? '+' : '-';
|
||||||
return $prefix . number_format(abs($this->amount)) . '원';
|
|
||||||
|
return $prefix.number_format(abs($this->amount)).'원';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,7 +117,7 @@ public function getFormattedAmountAttribute(): string
|
|||||||
*/
|
*/
|
||||||
public function getFormattedBalanceAfterAttribute(): string
|
public function getFormattedBalanceAfterAttribute(): string
|
||||||
{
|
{
|
||||||
return number_format($this->balance_after) . '원';
|
return number_format($this->balance_after).'원';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Models\Finance;
|
namespace App\Models\Finance;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Models\Finance;
|
namespace App\Models\Finance;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Models\Finance;
|
namespace App\Models\Finance;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Models\Finance;
|
namespace App\Models\Finance;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|||||||
@@ -18,17 +18,23 @@ class FundSchedule extends Model
|
|||||||
|
|
||||||
// 일정 유형 상수
|
// 일정 유형 상수
|
||||||
public const TYPE_INCOME = 'income';
|
public const TYPE_INCOME = 'income';
|
||||||
|
|
||||||
public const TYPE_EXPENSE = 'expense';
|
public const TYPE_EXPENSE = 'expense';
|
||||||
|
|
||||||
// 상태 상수
|
// 상태 상수
|
||||||
public const STATUS_PENDING = 'pending';
|
public const STATUS_PENDING = 'pending';
|
||||||
|
|
||||||
public const STATUS_COMPLETED = 'completed';
|
public const STATUS_COMPLETED = 'completed';
|
||||||
|
|
||||||
public const STATUS_CANCELLED = 'cancelled';
|
public const STATUS_CANCELLED = 'cancelled';
|
||||||
|
|
||||||
// 반복 규칙 상수
|
// 반복 규칙 상수
|
||||||
public const RECURRENCE_DAILY = 'daily';
|
public const RECURRENCE_DAILY = 'daily';
|
||||||
|
|
||||||
public const RECURRENCE_WEEKLY = 'weekly';
|
public const RECURRENCE_WEEKLY = 'weekly';
|
||||||
|
|
||||||
public const RECURRENCE_MONTHLY = 'monthly';
|
public const RECURRENCE_MONTHLY = 'monthly';
|
||||||
|
|
||||||
public const RECURRENCE_YEARLY = 'yearly';
|
public const RECURRENCE_YEARLY = 'yearly';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
@@ -102,14 +108,14 @@ public function getFormattedAmountAttribute(): string
|
|||||||
$amount = abs($this->amount);
|
$amount = abs($this->amount);
|
||||||
|
|
||||||
if ($amount >= 100000000) {
|
if ($amount >= 100000000) {
|
||||||
return number_format($amount / 100000000, 1) . '억원';
|
return number_format($amount / 100000000, 1).'억원';
|
||||||
} elseif ($amount >= 10000000) {
|
} elseif ($amount >= 10000000) {
|
||||||
return number_format($amount / 10000000, 1) . '천만원';
|
return number_format($amount / 10000000, 1).'천만원';
|
||||||
} elseif ($amount >= 10000) {
|
} elseif ($amount >= 10000) {
|
||||||
return number_format($amount / 10000, 0) . '만원';
|
return number_format($amount / 10000, 0).'만원';
|
||||||
}
|
}
|
||||||
|
|
||||||
return number_format($amount) . '원';
|
return number_format($amount).'원';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Models\Finance;
|
namespace App\Models\Finance;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Models\Finance;
|
namespace App\Models\Finance;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Models\Finance;
|
namespace App\Models\Finance;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Models\Finance;
|
namespace App\Models\Finance;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|||||||
@@ -29,12 +29,17 @@ class Item extends Model
|
|||||||
|
|
||||||
// 유형 상수
|
// 유형 상수
|
||||||
const TYPE_FG = 'FG'; // 완제품
|
const TYPE_FG = 'FG'; // 완제품
|
||||||
|
|
||||||
const TYPE_PT = 'PT'; // 부품
|
const TYPE_PT = 'PT'; // 부품
|
||||||
|
|
||||||
const TYPE_SM = 'SM'; // 부자재
|
const TYPE_SM = 'SM'; // 부자재
|
||||||
|
|
||||||
const TYPE_RM = 'RM'; // 원자재
|
const TYPE_RM = 'RM'; // 원자재
|
||||||
|
|
||||||
const TYPE_CS = 'CS'; // 소모품
|
const TYPE_CS = 'CS'; // 소모품
|
||||||
|
|
||||||
const PRODUCT_TYPES = ['FG', 'PT'];
|
const PRODUCT_TYPES = ['FG', 'PT'];
|
||||||
|
|
||||||
const MATERIAL_TYPES = ['SM', 'RM', 'CS'];
|
const MATERIAL_TYPES = ['SM', 'RM', 'CS'];
|
||||||
|
|
||||||
// ── 관계 ──
|
// ── 관계 ──
|
||||||
@@ -73,7 +78,10 @@ public function scopeActive($query)
|
|||||||
|
|
||||||
public function scopeSearch($query, ?string $search)
|
public function scopeSearch($query, ?string $search)
|
||||||
{
|
{
|
||||||
if (!$search) return $query;
|
if (! $search) {
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
return $query->where(function ($q) use ($search) {
|
return $query->where(function ($q) use ($search) {
|
||||||
$q->where('code', 'like', "%{$search}%")
|
$q->where('code', 'like', "%{$search}%")
|
||||||
->orWhere('name', 'like', "%{$search}%");
|
->orWhere('name', 'like', "%{$search}%");
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public function constructionSitePhoto(): BelongsTo
|
|||||||
|
|
||||||
public function hasPhoto(string $type): bool
|
public function hasPhoto(string $type): bool
|
||||||
{
|
{
|
||||||
return !empty($this->{$type . '_photo_path'});
|
return ! empty($this->{$type.'_photo_path'});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPhotoCount(): int
|
public function getPhotoCount(): int
|
||||||
|
|||||||
@@ -16,9 +16,13 @@ class MeetingMinute extends Model
|
|||||||
protected $table = 'meeting_minutes';
|
protected $table = 'meeting_minutes';
|
||||||
|
|
||||||
const STATUS_DRAFT = 'DRAFT';
|
const STATUS_DRAFT = 'DRAFT';
|
||||||
|
|
||||||
const STATUS_RECORDING = 'RECORDING';
|
const STATUS_RECORDING = 'RECORDING';
|
||||||
|
|
||||||
const STATUS_PROCESSING = 'PROCESSING';
|
const STATUS_PROCESSING = 'PROCESSING';
|
||||||
|
|
||||||
const STATUS_COMPLETED = 'COMPLETED';
|
const STATUS_COMPLETED = 'COMPLETED';
|
||||||
|
|
||||||
const STATUS_FAILED = 'FAILED';
|
const STATUS_FAILED = 'FAILED';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|||||||
@@ -30,9 +30,13 @@ class NumberingRule extends Model
|
|||||||
// SoftDeletes 없음 (DB에 deleted_at 컬럼 없음) → Hard Delete
|
// SoftDeletes 없음 (DB에 deleted_at 컬럼 없음) → Hard Delete
|
||||||
|
|
||||||
const DOC_QUOTE = 'quote';
|
const DOC_QUOTE = 'quote';
|
||||||
|
|
||||||
const DOC_ORDER = 'order';
|
const DOC_ORDER = 'order';
|
||||||
|
|
||||||
const DOC_SALE = 'sale';
|
const DOC_SALE = 'sale';
|
||||||
|
|
||||||
const DOC_WORK_ORDER = 'work_order';
|
const DOC_WORK_ORDER = 'work_order';
|
||||||
|
|
||||||
const DOC_MATERIAL_RECEIPT = 'material_receipt';
|
const DOC_MATERIAL_RECEIPT = 'material_receipt';
|
||||||
|
|
||||||
public static function documentTypes(): array
|
public static function documentTypes(): array
|
||||||
@@ -68,8 +72,8 @@ public function getPreviewAttribute(): string
|
|||||||
'static' => $segment['value'] ?? '',
|
'static' => $segment['value'] ?? '',
|
||||||
'separator' => $segment['value'] ?? '',
|
'separator' => $segment['value'] ?? '',
|
||||||
'date' => now()->format($segment['format'] ?? 'ymd'),
|
'date' => now()->format($segment['format'] ?? 'ymd'),
|
||||||
'param' => $segment['default'] ?? '{' . ($segment['key'] ?? '?') . '}',
|
'param' => $segment['default'] ?? '{'.($segment['key'] ?? '?').'}',
|
||||||
'mapping' => $segment['default'] ?? '{' . ($segment['key'] ?? '?') . '}',
|
'mapping' => $segment['default'] ?? '{'.($segment['key'] ?? '?').'}',
|
||||||
'sequence' => str_pad('1', $this->sequence_padding, '0', STR_PAD_LEFT),
|
'sequence' => str_pad('1', $this->sequence_padding, '0', STR_PAD_LEFT),
|
||||||
default => '',
|
default => '',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,17 +61,17 @@ public function getParsedOsVersionAttribute(): ?string
|
|||||||
|
|
||||||
// Android 버전 추출
|
// Android 버전 추출
|
||||||
if (preg_match('/Android\s+([\d.]+)/', $this->device_name, $matches)) {
|
if (preg_match('/Android\s+([\d.]+)/', $this->device_name, $matches)) {
|
||||||
return 'Android ' . $matches[1];
|
return 'Android '.$matches[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// iOS 버전 추출
|
// iOS 버전 추출
|
||||||
if (preg_match('/iPhone\s+OS\s+([\d_]+)/', $this->device_name, $matches)) {
|
if (preg_match('/iPhone\s+OS\s+([\d_]+)/', $this->device_name, $matches)) {
|
||||||
return 'iOS ' . str_replace('_', '.', $matches[1]);
|
return 'iOS '.str_replace('_', '.', $matches[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// iPad 버전 추출
|
// iPad 버전 추출
|
||||||
if (preg_match('/CPU\s+OS\s+([\d_]+)/', $this->device_name, $matches)) {
|
if (preg_match('/CPU\s+OS\s+([\d_]+)/', $this->device_name, $matches)) {
|
||||||
return 'iOS ' . str_replace('_', '.', $matches[1]);
|
return 'iOS '.str_replace('_', '.', $matches[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -94,6 +94,7 @@ private function parseUserAgent(string $userAgent): string
|
|||||||
if (($pos = strpos($model, ' Build')) !== false) {
|
if (($pos = strpos($model, ' Build')) !== false) {
|
||||||
$model = substr($model, 0, $pos);
|
$model = substr($model, 0, $pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $model ?: 'Android Device';
|
return $model ?: 'Android Device';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,14 +51,18 @@ class SalesCommission extends Model
|
|||||||
* 상태 상수
|
* 상태 상수
|
||||||
*/
|
*/
|
||||||
const STATUS_PENDING = 'pending';
|
const STATUS_PENDING = 'pending';
|
||||||
|
|
||||||
const STATUS_APPROVED = 'approved';
|
const STATUS_APPROVED = 'approved';
|
||||||
|
|
||||||
const STATUS_PAID = 'paid';
|
const STATUS_PAID = 'paid';
|
||||||
|
|
||||||
const STATUS_CANCELLED = 'cancelled';
|
const STATUS_CANCELLED = 'cancelled';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 입금 구분 상수
|
* 입금 구분 상수
|
||||||
*/
|
*/
|
||||||
const PAYMENT_DEPOSIT = 'deposit';
|
const PAYMENT_DEPOSIT = 'deposit';
|
||||||
|
|
||||||
const PAYMENT_BALANCE = 'balance';
|
const PAYMENT_BALANCE = 'balance';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -361,7 +365,7 @@ public function recordFirstPayment(?Carbon $paymentDate = null): bool
|
|||||||
*/
|
*/
|
||||||
public function recordFirstPartnerPaid(?Carbon $paidDate = null): bool
|
public function recordFirstPartnerPaid(?Carbon $paidDate = null): bool
|
||||||
{
|
{
|
||||||
if (!$this->first_payment_at) {
|
if (! $this->first_payment_at) {
|
||||||
return false; // 1차 납입이 먼저 완료되어야 함
|
return false; // 1차 납입이 먼저 완료되어야 함
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,7 +389,7 @@ public function recordSecondPayment(?Carbon $paymentDate = null): bool
|
|||||||
*/
|
*/
|
||||||
public function recordSecondPartnerPaid(?Carbon $paidDate = null): bool
|
public function recordSecondPartnerPaid(?Carbon $paidDate = null): bool
|
||||||
{
|
{
|
||||||
if (!$this->second_payment_at) {
|
if (! $this->second_payment_at) {
|
||||||
return false; // 2차 납입이 먼저 완료되어야 함
|
return false; // 2차 납입이 먼저 완료되어야 함
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +413,7 @@ public function recordFirstSubscription(?Carbon $subscriptionDate = null): bool
|
|||||||
*/
|
*/
|
||||||
public function recordManagerPaid(?Carbon $paidDate = null): bool
|
public function recordManagerPaid(?Carbon $paidDate = null): bool
|
||||||
{
|
{
|
||||||
if (!$this->first_subscription_at) {
|
if (! $this->first_subscription_at) {
|
||||||
return false; // 첫 구독료 입금이 먼저 완료되어야 함
|
return false; // 첫 구독료 입금이 먼저 완료되어야 함
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,9 +427,10 @@ public function recordManagerPaid(?Carbon $paidDate = null): bool
|
|||||||
*/
|
*/
|
||||||
public function getFirstPartnerScheduledDateAttribute(): ?Carbon
|
public function getFirstPartnerScheduledDateAttribute(): ?Carbon
|
||||||
{
|
{
|
||||||
if (!$this->first_payment_at) {
|
if (! $this->first_payment_at) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Carbon::parse($this->first_payment_at)->addMonth()->day(10);
|
return Carbon::parse($this->first_payment_at)->addMonth()->day(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,9 +439,10 @@ public function getFirstPartnerScheduledDateAttribute(): ?Carbon
|
|||||||
*/
|
*/
|
||||||
public function getSecondPartnerScheduledDateAttribute(): ?Carbon
|
public function getSecondPartnerScheduledDateAttribute(): ?Carbon
|
||||||
{
|
{
|
||||||
if (!$this->second_payment_at) {
|
if (! $this->second_payment_at) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Carbon::parse($this->second_payment_at)->addMonth()->day(10);
|
return Carbon::parse($this->second_payment_at)->addMonth()->day(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,9 +451,10 @@ public function getSecondPartnerScheduledDateAttribute(): ?Carbon
|
|||||||
*/
|
*/
|
||||||
public function getManagerScheduledDateAttribute(): ?Carbon
|
public function getManagerScheduledDateAttribute(): ?Carbon
|
||||||
{
|
{
|
||||||
if (!$this->first_subscription_at) {
|
if (! $this->first_subscription_at) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Carbon::parse($this->first_subscription_at)->addMonth()->day(10);
|
return Carbon::parse($this->first_subscription_at)->addMonth()->day(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,7 +487,7 @@ public function isManagerPaid(): bool
|
|||||||
*/
|
*/
|
||||||
public function recordFirstReferrerPaid(?Carbon $paidDate = null): bool
|
public function recordFirstReferrerPaid(?Carbon $paidDate = null): bool
|
||||||
{
|
{
|
||||||
if (!$this->referrer_partner_id) {
|
if (! $this->referrer_partner_id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,7 +501,7 @@ public function recordFirstReferrerPaid(?Carbon $paidDate = null): bool
|
|||||||
*/
|
*/
|
||||||
public function recordSecondReferrerPaid(?Carbon $paidDate = null): bool
|
public function recordSecondReferrerPaid(?Carbon $paidDate = null): bool
|
||||||
{
|
{
|
||||||
if (!$this->referrer_partner_id) {
|
if (! $this->referrer_partner_id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Models\Sales;
|
namespace App\Models\Sales;
|
||||||
|
|
||||||
use App\Models\Sales\TenantProspect;
|
|
||||||
use App\Models\Tenants\Tenant;
|
use App\Models\Tenants\Tenant;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@@ -61,7 +60,9 @@ class SalesConsultation extends Model
|
|||||||
* 상담 유형 상수
|
* 상담 유형 상수
|
||||||
*/
|
*/
|
||||||
const TYPE_TEXT = 'text';
|
const TYPE_TEXT = 'text';
|
||||||
|
|
||||||
const TYPE_AUDIO = 'audio';
|
const TYPE_AUDIO = 'audio';
|
||||||
|
|
||||||
const TYPE_FILE = 'file';
|
const TYPE_FILE = 'file';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,7 +186,7 @@ public function deleteWithFile(): bool
|
|||||||
*/
|
*/
|
||||||
public function getFormattedDurationAttribute(): ?string
|
public function getFormattedDurationAttribute(): ?string
|
||||||
{
|
{
|
||||||
if (!$this->duration) {
|
if (! $this->duration) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,16 +201,16 @@ public function getFormattedDurationAttribute(): ?string
|
|||||||
*/
|
*/
|
||||||
public function getFormattedFileSizeAttribute(): ?string
|
public function getFormattedFileSizeAttribute(): ?string
|
||||||
{
|
{
|
||||||
if (!$this->file_size) {
|
if (! $this->file_size) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->file_size < 1024) {
|
if ($this->file_size < 1024) {
|
||||||
return $this->file_size . ' B';
|
return $this->file_size.' B';
|
||||||
} elseif ($this->file_size < 1024 * 1024) {
|
} elseif ($this->file_size < 1024 * 1024) {
|
||||||
return round($this->file_size / 1024, 1) . ' KB';
|
return round($this->file_size / 1024, 1).' KB';
|
||||||
} else {
|
} else {
|
||||||
return round($this->file_size / (1024 * 1024), 1) . ' MB';
|
return round($this->file_size / (1024 * 1024), 1).' MB';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,14 +76,14 @@ public function getFormattedSizeAttribute(): string
|
|||||||
$bytes = $this->file_size;
|
$bytes = $this->file_size;
|
||||||
|
|
||||||
if ($bytes >= 1073741824) {
|
if ($bytes >= 1073741824) {
|
||||||
return number_format($bytes / 1073741824, 2) . ' GB';
|
return number_format($bytes / 1073741824, 2).' GB';
|
||||||
} elseif ($bytes >= 1048576) {
|
} elseif ($bytes >= 1048576) {
|
||||||
return number_format($bytes / 1048576, 2) . ' MB';
|
return number_format($bytes / 1048576, 2).' MB';
|
||||||
} elseif ($bytes >= 1024) {
|
} elseif ($bytes >= 1024) {
|
||||||
return number_format($bytes / 1024, 2) . ' KB';
|
return number_format($bytes / 1024, 2).' KB';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $bytes . ' bytes';
|
return $bytes.' bytes';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,7 +123,7 @@ public function existsInStorage(): bool
|
|||||||
*/
|
*/
|
||||||
public function getUrl(): ?string
|
public function getUrl(): ?string
|
||||||
{
|
{
|
||||||
if (!$this->existsInStorage()) {
|
if (! $this->existsInStorage()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ public function getUrl(): ?string
|
|||||||
*/
|
*/
|
||||||
public function download()
|
public function download()
|
||||||
{
|
{
|
||||||
if (!$this->existsInStorage()) {
|
if (! $this->existsInStorage()) {
|
||||||
abort(404, '파일을 찾을 수 없습니다.');
|
abort(404, '파일을 찾을 수 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Models\Sales;
|
namespace App\Models\Sales;
|
||||||
|
|
||||||
use App\Models\Sales\SalesCommission;
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@@ -132,7 +131,7 @@ public static function generatePartnerCode(): string
|
|||||||
|
|
||||||
$sequence = $lastPartner ? (int) substr($lastPartner->partner_code, -4) + 1 : 1;
|
$sequence = $lastPartner ? (int) substr($lastPartner->partner_code, -4) + 1 : 1;
|
||||||
|
|
||||||
return $prefix . $year . str_pad($sequence, 4, '0', STR_PAD_LEFT);
|
return $prefix.$year.str_pad($sequence, 4, '0', STR_PAD_LEFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user