fix: [sales] 무료 체험 기간을 1주일(7일)로 변경
- 가격 시뮬레이터: 1/2/3/6개월 선택 → 없음/1주일 토글로 변경 - promoFreeMonths → promoFreeTrial (boolean)으로 변환 - 연 구독료 계산: 1주일분(7/30) 차감 방식 적용 - 데모 테넌트: free_months → free_trial 필드 변경 - 계약 컨트롤러: validation 규칙 업데이트
This commit is contained in:
@@ -150,7 +150,7 @@ public function store(Request $request)
|
||||
'promo_dev_amount' => 'nullable|integer|min:0|max:5000000',
|
||||
'promo_dev_waive' => 'nullable|in:0,1',
|
||||
'promo_sub_percent' => 'nullable|integer|min:0|max:50',
|
||||
'promo_free_months' => 'nullable|integer|in:0,1,2,3,6',
|
||||
'promo_free_trial' => 'nullable|in:0,1',
|
||||
'promo_note' => 'nullable|string|max:200',
|
||||
]);
|
||||
|
||||
@@ -173,7 +173,7 @@ public function store(Request $request)
|
||||
'dev_discount_amount' => (int) $request->input('promo_dev_amount', 0),
|
||||
'dev_waive' => $request->input('promo_dev_waive') === '1',
|
||||
'sub_discount_percent' => (int) $request->input('promo_sub_percent', 0),
|
||||
'free_months' => (int) $request->input('promo_free_months', 0),
|
||||
'free_trial' => $request->input('promo_free_trial') === '1',
|
||||
'note' => $request->input('promo_note', ''),
|
||||
'applied_at' => now()->toDateTimeString(),
|
||||
'applied_by_user_id' => auth()->id(),
|
||||
|
||||
@@ -35,7 +35,7 @@ public function saveProducts(Request $request): JsonResponse
|
||||
'promotion.dev_discount_amount' => 'nullable|numeric|min:0',
|
||||
'promotion.dev_waive' => 'nullable|boolean',
|
||||
'promotion.sub_discount_percent' => 'nullable|numeric|min:0|max:50',
|
||||
'promotion.free_months' => 'nullable|integer|in:0,1,2,3,6',
|
||||
'promotion.free_trial' => 'nullable|boolean',
|
||||
'promotion.note' => 'nullable|string|max:200',
|
||||
]);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
promoDevAmount: 0,
|
||||
promoDevWaive: false,
|
||||
promoSubPercent: 0,
|
||||
promoFreeMonths: 0,
|
||||
promoFreeTrial: false,
|
||||
promoNote: '',
|
||||
|
||||
// 개발비 할인 최대값
|
||||
@@ -35,7 +35,7 @@
|
||||
parts.push('개발비 ' + Number(this.promoDevAmount).toLocaleString() + '원 할인');
|
||||
}
|
||||
if (this.promoSubPercent > 0) parts.push('구독료 ' + this.promoSubPercent + '% 할인');
|
||||
if (this.promoFreeMonths > 0) parts.push('무료 ' + this.promoFreeMonths + '개월');
|
||||
if (this.promoFreeTrial) parts.push('무료 체험 1주일');
|
||||
return parts.length ? parts.join(' / ') : '없음';
|
||||
},
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
// 프로모션 적용 여부
|
||||
get hasPromo() {
|
||||
return this.promoDevWaive || this.promoDevPercent > 0 || this.promoDevAmount > 0 || this.promoSubPercent > 0 || this.promoFreeMonths > 0;
|
||||
return this.promoDevWaive || this.promoDevPercent > 0 || this.promoDevAmount > 0 || this.promoSubPercent > 0 || this.promoFreeTrial;
|
||||
}
|
||||
}">
|
||||
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity" onclick="closeModal()"></div>
|
||||
@@ -217,17 +217,20 @@ class="flex-1 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-e
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 무료 기간 -->
|
||||
<!-- 무료 체험 (1주일) -->
|
||||
<div class="bg-gray-50 rounded-lg p-4 space-y-2">
|
||||
<h4 class="text-sm font-semibold text-gray-700">무료 사용 기간</h4>
|
||||
<h4 class="text-sm font-semibold text-gray-700">무료 체험 (1주일)</h4>
|
||||
<div class="flex gap-2">
|
||||
<template x-for="m in [0, 1, 2, 3, 6]" :key="m">
|
||||
<button type="button" @click="promoFreeMonths = m"
|
||||
:class="promoFreeMonths === m ? 'bg-violet-600 text-white' : 'bg-white text-gray-600 border border-gray-300'"
|
||||
class="px-3 py-1.5 rounded-lg text-xs font-medium transition">
|
||||
<span x-text="m === 0 ? '없음' : m + '개월'"></span>
|
||||
</button>
|
||||
</template>
|
||||
<button type="button" @click="promoFreeTrial = false"
|
||||
:class="!promoFreeTrial ? 'bg-violet-600 text-white' : 'bg-white text-gray-600 border border-gray-300'"
|
||||
class="px-3 py-1.5 rounded-lg text-xs font-medium transition">
|
||||
없음
|
||||
</button>
|
||||
<button type="button" @click="promoFreeTrial = true"
|
||||
:class="promoFreeTrial ? 'bg-violet-600 text-white' : 'bg-white text-gray-600 border border-gray-300'"
|
||||
class="px-3 py-1.5 rounded-lg text-xs font-medium transition">
|
||||
1주일 (7일)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -258,7 +261,7 @@ class="w-full px-3 py-1.5 border border-gray-300 rounded-lg text-sm focus:ring-2
|
||||
<input type="hidden" name="promo_dev_amount" :value="promoDevAmount">
|
||||
<input type="hidden" name="promo_dev_waive" :value="promoDevWaive ? '1' : '0'">
|
||||
<input type="hidden" name="promo_sub_percent" :value="promoSubPercent">
|
||||
<input type="hidden" name="promo_free_months" :value="promoFreeMonths">
|
||||
<input type="hidden" name="promo_free_trial" :value="promoFreeTrial ? '1' : '0'">
|
||||
<input type="hidden" name="promo_note" :value="promoNote">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -122,11 +122,11 @@
|
||||
{{ ($promo['sub_discount_percent'] ?? 0) > 0 ? $promo['sub_discount_percent'] . '%' : '없음' }}
|
||||
</span>
|
||||
</div>
|
||||
{{-- 무료 기간 --}}
|
||||
{{-- 무료 체험 --}}
|
||||
<div class="flex justify-between bg-white rounded px-3 py-2">
|
||||
<span class="text-sm text-gray-600">무료 사용 기간</span>
|
||||
<span class="text-sm font-bold {{ ($promo['free_months'] ?? 0) > 0 ? 'text-violet-700' : 'text-gray-400' }}">
|
||||
{{ ($promo['free_months'] ?? 0) > 0 ? $promo['free_months'] . '개월' : '없음' }}
|
||||
<span class="text-sm text-gray-600">무료 체험</span>
|
||||
<span class="text-sm font-bold {{ !empty($promo['free_trial']) ? 'text-violet-700' : 'text-gray-400' }}">
|
||||
{{ !empty($promo['free_trial']) ? '1주일(7일)' : '없음' }}
|
||||
</span>
|
||||
</div>
|
||||
{{-- 메모 --}}
|
||||
|
||||
@@ -490,7 +490,7 @@ function productSelection() {
|
||||
promoDevAmount: {{ $savedPromotion['dev_discount_amount'] ?? 0 }},
|
||||
promoDevWaive: {{ !empty($savedPromotion['dev_waive']) ? 'true' : 'false' }},
|
||||
promoSubPercent: {{ $savedPromotion['sub_discount_percent'] ?? 0 }},
|
||||
promoFreeMonths: {{ $savedPromotion['free_months'] ?? 0 }},
|
||||
promoFreeTrial: {{ !empty($savedPromotion['free_trial']) ? 'true' : 'false' }},
|
||||
promoNote: '{{ addslashes($savedPromotion['note'] ?? '') }}',
|
||||
|
||||
isSelected(id) {
|
||||
@@ -713,7 +713,7 @@ function productSelection() {
|
||||
dev_discount_amount: this.promoDevAmount,
|
||||
dev_waive: this.promoDevWaive,
|
||||
sub_discount_percent: this.promoSubPercent,
|
||||
free_months: this.promoFreeMonths,
|
||||
free_trial: this.promoFreeTrial,
|
||||
note: this.promoNote,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -620,13 +620,13 @@ class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-70
|
||||
<div class="bg-white rounded-xl p-5 border border-gray-100">
|
||||
<h3 class="font-bold text-gray-900 mb-3">3. 무료 체험 기간</h3>
|
||||
<p class="text-sm text-gray-700 leading-relaxed mb-2">
|
||||
서비스 시작 후 <strong>처음 몇 개월간 구독료를 면제</strong>해 줄 수 있습니다.
|
||||
서비스 시작 후 <strong>첫 1주일(7일) 구독료를 면제</strong>해 줄 수 있습니다.
|
||||
</p>
|
||||
<div class="bg-gray-50 rounded-lg p-4 text-sm space-y-2">
|
||||
<p class="text-gray-600">예: 3개월 무료 체험 설정 시</p>
|
||||
<p class="text-gray-600">→ 1~3월: 구독료 0원 / 4~12월: 구독료 정상 부과</p>
|
||||
<p class="text-gray-600">→ 1년차 총 비용 = 개발비 + <strong>9개월</strong> 구독료</p>
|
||||
<p class="text-gray-500 text-xs mt-1">선택 가능: 없음, 1개월, 2개월, 3개월, 6개월</p>
|
||||
<p class="text-gray-600">예: 무료 체험 적용 시</p>
|
||||
<p class="text-gray-600">→ 첫 7일: 구독료 0원 / 8일째부터: 구독료 정상 부과</p>
|
||||
<p class="text-gray-600">→ 1년차 총 비용 = 개발비 + 약 <strong>11.75개월</strong> 구독료</p>
|
||||
<p class="text-gray-500 text-xs mt-1">선택 가능: 없음, 1주일(7일)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -977,22 +977,27 @@ class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-o
|
||||
</template>
|
||||
</div>
|
||||
|
||||
{{-- 무료 체험 기간 --}}
|
||||
{{-- 무료 체험 기간 (1주일 고정) --}}
|
||||
<div class="border-t border-gray-100 pt-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">무료 체험 기간</label>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">무료 체험 (1주일)</label>
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
<template x-for="m in [0, 1, 2, 3, 6]" :key="m">
|
||||
<button type="button" x-on:click="promoFreeMonths = m"
|
||||
class="px-3 py-1.5 text-xs rounded-lg transition-colors border"
|
||||
:class="promoFreeMonths === m
|
||||
? 'bg-orange-500 text-white border-orange-500'
|
||||
: 'bg-white text-gray-600 border-gray-200 hover:border-orange-300'">
|
||||
<span x-text="m === 0 ? '없음' : m + '개월'"></span>
|
||||
</button>
|
||||
</template>
|
||||
<button type="button" x-on:click="promoFreeTrial = false"
|
||||
class="px-3 py-1.5 text-xs rounded-lg transition-colors border"
|
||||
:class="!promoFreeTrial
|
||||
? 'bg-orange-500 text-white border-orange-500'
|
||||
: 'bg-white text-gray-600 border-gray-200 hover:border-orange-300'">
|
||||
없음
|
||||
</button>
|
||||
<button type="button" x-on:click="promoFreeTrial = true"
|
||||
class="px-3 py-1.5 text-xs rounded-lg transition-colors border"
|
||||
:class="promoFreeTrial
|
||||
? 'bg-orange-500 text-white border-orange-500'
|
||||
: 'bg-white text-gray-600 border-gray-200 hover:border-orange-300'">
|
||||
1주일 (7일)
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-xs text-gray-400 mt-1" x-show="promoFreeMonths > 0">
|
||||
첫 <span x-text="promoFreeMonths"></span>개월 구독료가 면제됩니다
|
||||
<p class="text-xs text-gray-400 mt-1" x-show="promoFreeTrial">
|
||||
첫 7일간 구독료가 면제됩니다
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1098,9 +1103,9 @@ class="w-full py-2 text-xs text-gray-500 bg-gray-50 hover:bg-gray-100 rounded-lg
|
||||
<span class="font-semibold text-gray-900" x-text="formatCurrency(finalSubscriptionFee()) + '/월'"></span>
|
||||
</div>
|
||||
{{-- 무료 체험 기간 안내 --}}
|
||||
<div class="flex justify-between items-center text-xs" x-show="promoFreeMonths > 0">
|
||||
<div class="flex justify-between items-center text-xs" x-show="promoFreeTrial">
|
||||
<span class="text-orange-500">무료 체험</span>
|
||||
<span class="text-orange-600 font-medium" x-text="'첫 ' + promoFreeMonths + '개월 무료'"></span>
|
||||
<span class="text-orange-600 font-medium">첫 1주일(7일) 무료</span>
|
||||
</div>
|
||||
{{-- 연 구독료 --}}
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
@@ -1116,9 +1121,9 @@ class="w-full py-2 text-xs text-gray-500 bg-gray-50 hover:bg-gray-100 rounded-lg
|
||||
<span class="font-bold text-xl text-emerald-700" x-text="formatCurrency(totalFirstYearCost())"></span>
|
||||
</div>
|
||||
<p class="text-xs text-gray-400 text-right mt-1">
|
||||
개발비 + <span x-text="12 - promoFreeMonths"></span>개월 구독료 (VAT 별도)
|
||||
<template x-if="promoFreeMonths > 0">
|
||||
<span class="text-orange-500"> | <span x-text="promoFreeMonths"></span>개월 무료 적용</span>
|
||||
개발비 + 12개월 구독료 (VAT 별도)
|
||||
<template x-if="promoFreeTrial">
|
||||
<span class="text-orange-500"> | 1주일 무료 체험 적용</span>
|
||||
</template>
|
||||
</p>
|
||||
{{-- 프로모션 미적용 대비 절감액 --}}
|
||||
@@ -1289,7 +1294,7 @@ function buildRequiredSelected(catId) {
|
||||
promoRegistrationAmount: 0,
|
||||
promoWaiveRegistration: false,
|
||||
promoSubscriptionPercent: 0,
|
||||
promoFreeMonths: 0,
|
||||
promoFreeTrial: false,
|
||||
promoNote: '',
|
||||
|
||||
// --- 카테고리 선택 (상호 배타) ---
|
||||
@@ -1418,7 +1423,7 @@ function buildRequiredSelected(catId) {
|
||||
return this.promoWaiveRegistration
|
||||
|| this.promoRegistrationAmount > 0
|
||||
|| this.promoSubscriptionPercent > 0
|
||||
|| this.promoFreeMonths > 0;
|
||||
|| this.promoFreeTrial;
|
||||
},
|
||||
|
||||
promoDevDiscountMax() {
|
||||
@@ -1481,8 +1486,12 @@ function buildRequiredSelected(catId) {
|
||||
},
|
||||
|
||||
annualSubscriptionFee() {
|
||||
const paidMonths = 12 - this.promoFreeMonths;
|
||||
return this.finalSubscriptionFee() * paidMonths;
|
||||
const annual = this.finalSubscriptionFee() * 12;
|
||||
if (this.promoFreeTrial) {
|
||||
// 1주일(7일) 무료: 월 구독료의 7/30 차감
|
||||
return Math.round(annual - this.finalSubscriptionFee() * 7 / 30);
|
||||
}
|
||||
return annual;
|
||||
},
|
||||
|
||||
totalFirstYearCost() {
|
||||
@@ -1521,7 +1530,7 @@ function buildRequiredSelected(catId) {
|
||||
this.promoRegistrationAmount = 0;
|
||||
this.promoWaiveRegistration = false;
|
||||
this.promoSubscriptionPercent = 0;
|
||||
this.promoFreeMonths = 0;
|
||||
this.promoFreeTrial = false;
|
||||
this.promoNote = '';
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user