- Phase 3.5.1: Multi-Package support in order form
- Phase 3.5.2: Invoice items form (PARCEL only) - ShipmentPackage model and migration - Updated CustomerOrderController to accept packages[] and items[] - Auto-calculate volumetric weight from dimensions - 5-step order form with conditional Invoice step Phase 3.5 — core complete" git push origin main
This commit is contained in:
parent
1c8fb2c9d9
commit
1e4a8532fa
@ -5,6 +5,8 @@ jQuery(document).ready(function($) {
|
||||
var countries = [];
|
||||
var priceData = null;
|
||||
var packages = []; // آرایه بستهها
|
||||
var invoiceItems = []; // آرایه اقلام گمرکی
|
||||
var MAX_ITEMS = 9; // حداکثر تعداد اقلام
|
||||
|
||||
// ─── Load Countries ───
|
||||
function loadCountries() {
|
||||
@ -276,6 +278,225 @@ jQuery(document).ready(function($) {
|
||||
removePackage(pkgId);
|
||||
});
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// ─── Invoice Items Management (برای PARCEL) ───
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
// افزودن یه قلم جدید
|
||||
function addInvoiceItem() {
|
||||
if (invoiceItems.length >= MAX_ITEMS) {
|
||||
alert('حداکثر ' + MAX_ITEMS + ' قلم میتوانید اضافه کنید.');
|
||||
return;
|
||||
}
|
||||
var itemNumber = invoiceItems.length + 1;
|
||||
var item = {
|
||||
id: 'item_' + Date.now() + '_' + itemNumber,
|
||||
number: itemNumber,
|
||||
description: '',
|
||||
hsCode: '',
|
||||
quantity: '',
|
||||
unitPrice: ''
|
||||
};
|
||||
invoiceItems.push(item);
|
||||
renderInvoiceItem(item);
|
||||
updateItemsSummary();
|
||||
}
|
||||
|
||||
// حذف یه قلم
|
||||
function removeInvoiceItem(itemId) {
|
||||
if (invoiceItems.length <= 1) {
|
||||
alert('حداقل باید یک قلم وجود داشته باشد.');
|
||||
return;
|
||||
}
|
||||
invoiceItems = invoiceItems.filter(function(i) { return i.id !== itemId; });
|
||||
invoiceItems.forEach(function(item, i) {
|
||||
item.number = i + 1;
|
||||
});
|
||||
renderAllInvoiceItems();
|
||||
updateItemsSummary();
|
||||
}
|
||||
|
||||
// رندر یه قلم
|
||||
function renderInvoiceItem(item) {
|
||||
var html = '<div class="ifnex-item-card" data-item-id="' + item.id + '" style="border: 1px solid #ddd; padding: 16px; margin-bottom: 12px; border-radius: 8px; background: #fafafa; position: relative;">';
|
||||
html += '<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">';
|
||||
html += '<strong style="color: #f37021; font-size: 14px;">📋 قلم #' + item.number + '</strong>';
|
||||
html += '<button type="button" class="ifnex-btn ifnex-btn-sm ifnex-btn-danger ifnex-remove-item" data-item-id="' + item.id + '" title="حذف قلم" style="padding: 4px 8px;">❌ حذف</button>';
|
||||
html += '</div>';
|
||||
html += '<div class="ifnex-form-grid">';
|
||||
|
||||
// توضیحات
|
||||
html += '<div class="ifnex-field full">';
|
||||
html += '<label>شرح کالا *</label>';
|
||||
html += '<input type="text" class="ifnex-item-description" data-item-id="' + item.id + '" placeholder="مثلاً Electronics PCB Board" value="' + (item.description || '') + '" required style="direction: ltr;">';
|
||||
html += '</div>';
|
||||
|
||||
// HS Code
|
||||
html += '<div class="ifnex-field">';
|
||||
html += '<label>کد گمرکی (HS Code) *</label>';
|
||||
html += '<input type="text" class="ifnex-item-hs-code" data-item-id="' + item.id + '" placeholder="مثلاً 8542390001" value="' + (item.hsCode || '') + '" required style="direction: ltr;">';
|
||||
html += '</div>';
|
||||
|
||||
// تعداد
|
||||
html += '<div class="ifnex-field">';
|
||||
html += '<label>تعداد *</label>';
|
||||
html += '<input type="number" class="ifnex-item-quantity" data-item-id="' + item.id + '" min="1" step="1" placeholder="مثلاً 100" value="' + (item.quantity || '') + '" required>';
|
||||
html += '</div>';
|
||||
|
||||
// قیمت واحد
|
||||
html += '<div class="ifnex-field">';
|
||||
html += '<label>قیمت واحد (USD) *</label>';
|
||||
html += '<input type="number" class="ifnex-item-unit-price" data-item-id="' + item.id + '" min="0" step="0.01" placeholder="مثلاً 1.10" value="' + (item.unitPrice || '') + '" required style="direction: ltr;">';
|
||||
html += '</div>';
|
||||
|
||||
// مجموع (محاسبه خودکار)
|
||||
html += '<div class="ifnex-field">';
|
||||
html += '<label>مجموع (USD)</label>';
|
||||
html += '<input type="text" class="ifnex-item-total-display" data-item-id="' + item.id + '" value="0.00" readonly style="background: #f0f9ff; color: #0369a1; font-weight: bold;">';
|
||||
html += '</div>';
|
||||
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
|
||||
$('#ifnex-items-container').append(html);
|
||||
}
|
||||
|
||||
// رندر همه اقلام
|
||||
function renderAllInvoiceItems() {
|
||||
$('#ifnex-items-container').empty();
|
||||
invoiceItems.forEach(function(item) {
|
||||
renderInvoiceItem(item);
|
||||
});
|
||||
}
|
||||
|
||||
// محاسبه مجموع یه قلم
|
||||
function calculateItemTotal(itemId) {
|
||||
var item = invoiceItems.find(function(i) { return i.id === itemId; });
|
||||
if (!item) return 0;
|
||||
var qty = parseFloat(item.quantity) || 0;
|
||||
var price = parseFloat(item.unitPrice) || 0;
|
||||
return qty * price;
|
||||
}
|
||||
|
||||
// بروزرسانی خلاصه اقلام
|
||||
function updateItemsSummary() {
|
||||
var total = 0;
|
||||
invoiceItems.forEach(function(item) {
|
||||
total += calculateItemTotal(item.id);
|
||||
});
|
||||
|
||||
$('#ifnex-item-count').text(invoiceItems.length);
|
||||
$('#ifnex-invoice-total').text(total.toFixed(2));
|
||||
|
||||
if (invoiceItems.length > 0) {
|
||||
$('#ifnex-items-summary').show();
|
||||
} else {
|
||||
$('#ifnex-items-summary').hide();
|
||||
}
|
||||
}
|
||||
|
||||
// Event: افزودن قلم
|
||||
$(document).on('click', '#ifnex-add-item', function() {
|
||||
addInvoiceItem();
|
||||
});
|
||||
|
||||
// Event: تغییر توضیحات
|
||||
$(document).on('input', '.ifnex-item-description', function() {
|
||||
var itemId = $(this).data('item-id');
|
||||
var item = invoiceItems.find(function(i) { return i.id === itemId; });
|
||||
if (item) item.description = $(this).val();
|
||||
});
|
||||
|
||||
// Event: تغییر HS Code
|
||||
$(document).on('input', '.ifnex-item-hs-code', function() {
|
||||
var itemId = $(this).data('item-id');
|
||||
var item = invoiceItems.find(function(i) { return i.id === itemId; });
|
||||
if (item) item.hsCode = $(this).val();
|
||||
});
|
||||
|
||||
// Event: تغییر تعداد (محاسبه مجموع)
|
||||
$(document).on('input', '.ifnex-item-quantity', function() {
|
||||
var itemId = $(this).data('item-id');
|
||||
var item = invoiceItems.find(function(i) { return i.id === itemId; });
|
||||
if (item) {
|
||||
item.quantity = $(this).val();
|
||||
var total = calculateItemTotal(itemId);
|
||||
$('.ifnex-item-total-display[data-item-id="' + itemId + '"]').val(total.toFixed(2));
|
||||
updateItemsSummary();
|
||||
}
|
||||
});
|
||||
|
||||
// Event: تغییر قیمت واحد (محاسبه مجموع)
|
||||
$(document).on('input', '.ifnex-item-unit-price', function() {
|
||||
var itemId = $(this).data('item-id');
|
||||
var item = invoiceItems.find(function(i) { return i.id === itemId; });
|
||||
if (item) {
|
||||
item.unitPrice = $(this).val();
|
||||
var total = calculateItemTotal(itemId);
|
||||
$('.ifnex-item-total-display[data-item-id="' + itemId + '"]').val(total.toFixed(2));
|
||||
updateItemsSummary();
|
||||
}
|
||||
});
|
||||
|
||||
// Event: حذف قلم
|
||||
$(document).on('click', '.ifnex-remove-item', function() {
|
||||
var itemId = $(this).data('item-id');
|
||||
removeInvoiceItem(itemId);
|
||||
});
|
||||
|
||||
// ─── بررسی نوع محموله و نمایش/مخفیکردن Step Invoice ───
|
||||
function updateInvoiceStepVisibility() {
|
||||
var type = $('#ifnex-type').val();
|
||||
if (type === 'PARCEL') {
|
||||
$('.ifnex-step-invoice').show();
|
||||
$('.ifnex-step-invoice-content').show();
|
||||
// اگه اقلام خالی هست، یه قلم اولیه اضافه کن
|
||||
if (invoiceItems.length === 0) {
|
||||
addInvoiceItem();
|
||||
}
|
||||
} else {
|
||||
$('.ifnex-step-invoice').hide();
|
||||
$('.ifnex-step-invoice-content').hide();
|
||||
// اگه کاربر توی Step 3 بود و نوع رو تغییر داد
|
||||
if (currentStep === 3) {
|
||||
goToStep(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('#ifnex-type').on('change', function() {
|
||||
updateInvoiceStepVisibility();
|
||||
});
|
||||
|
||||
// اعتبارسنجی اقلام گمرکی
|
||||
function validateStep3() {
|
||||
if ($('#ifnex-type').val() !== 'PARCEL') {
|
||||
return true; // اگه PARCEL نیست، این مرحله نیست
|
||||
}
|
||||
|
||||
if (invoiceItems.length === 0) {
|
||||
showFormError('حداقل باید یک قلم اضافه کنید.');
|
||||
return false;
|
||||
}
|
||||
|
||||
var allValid = true;
|
||||
invoiceItems.forEach(function(item) {
|
||||
if (!item.description || !item.hsCode || !item.quantity || !item.unitPrice) {
|
||||
allValid = false;
|
||||
if (!item.description) $('.ifnex-item-description[data-item-id="' + item.id + '"]').css('border-color', '#ef4444');
|
||||
if (!item.hsCode) $('.ifnex-item-hs-code[data-item-id="' + item.id + '"]').css('border-color', '#ef4444');
|
||||
if (!item.quantity) $('.ifnex-item-quantity[data-item-id="' + item.id + '"]').css('border-color', '#ef4444');
|
||||
if (!item.unitPrice) $('.ifnex-item-unit-price[data-item-id="' + item.id + '"]').css('border-color', '#ef4444');
|
||||
}
|
||||
});
|
||||
|
||||
if (!allValid) {
|
||||
showFormError('تمامی فیلدهای اقلام گمرکی را تکمیل کنید.');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ─── Step Navigation ───
|
||||
function goToStep(step) {
|
||||
currentStep = step;
|
||||
@ -293,12 +514,24 @@ jQuery(document).ready(function($) {
|
||||
var next = currentStep + 1;
|
||||
if (currentStep === 1 && !validateStep1()) return;
|
||||
if (currentStep === 2 && !validateStep2()) return;
|
||||
if (currentStep === 3) { calculatePrice(); return; }
|
||||
if (currentStep === 3 && !validateStep3()) return;
|
||||
if (currentStep === 4) { calculatePrice(); return; }
|
||||
// اگه PARCEL نیست، از Step 2 مستقیم به Step 4 بپر
|
||||
if (currentStep === 2 && $('#ifnex-type').val() !== 'PARCEL') {
|
||||
goToStep(4);
|
||||
return;
|
||||
}
|
||||
goToStep(next);
|
||||
});
|
||||
|
||||
$('.ifnex-prev-step').on('click', function() {
|
||||
goToStep(currentStep - 1);
|
||||
var prev = currentStep - 1;
|
||||
// اگه PARCEL نیست، از Step 4 مستقیم به Step 2 برگرد
|
||||
if (currentStep === 4 && $('#ifnex-type').val() !== 'PARCEL') {
|
||||
goToStep(2);
|
||||
return;
|
||||
}
|
||||
goToStep(prev);
|
||||
});
|
||||
|
||||
function validateStep1() {
|
||||
@ -402,6 +635,19 @@ jQuery(document).ready(function($) {
|
||||
};
|
||||
});
|
||||
|
||||
// ساخت آرایه اقلام گمرکی برای ارسال به API (فقط اگه PARCEL هست)
|
||||
var itemsArray = [];
|
||||
if ($('#ifnex-type').val() === 'PARCEL') {
|
||||
itemsArray = invoiceItems.map(function(item) {
|
||||
return {
|
||||
description: item.description || '',
|
||||
hs_code: item.hsCode || '',
|
||||
quantity: parseInt(item.quantity) || 0,
|
||||
unit_price: parseFloat(item.unitPrice) || 0
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
direction: $('#ifnex-direction').val(),
|
||||
type: $('#ifnex-type').val(),
|
||||
@ -414,6 +660,7 @@ jQuery(document).ready(function($) {
|
||||
volumetric_weight: totalVolumetric,
|
||||
chargeable_weight: chargeableWeight,
|
||||
packages: packagesArray,
|
||||
items: itemsArray,
|
||||
discount_code: $('#ifnex-discount-code').val() || '',
|
||||
sender_name: $('#ifnex-sender-name').val(),
|
||||
sender_phone: $('#ifnex-sender-phone').val(),
|
||||
@ -432,6 +679,18 @@ jQuery(document).ready(function($) {
|
||||
html += '<div class="ifnex-price-row"><span>مجموع وزن واقعی:</span><strong>' + numberFormat(data.weight || 0) + ' kg</strong></div>';
|
||||
html += '<div class="ifnex-price-row"><span>مجموع وزن حجمی:</span><strong>' + numberFormat(data.volumetric_weight || 0) + ' kg</strong></div>';
|
||||
html += '<div class="ifnex-price-row"><span>وزن قابل محاسبه:</span><strong>' + numberFormat(data.chargeable_weight || 0) + ' kg</strong></div>';
|
||||
|
||||
// نمایش اقلام گمرکی (اگه PARCEL هست)
|
||||
if ($('#ifnex-type').val() === 'PARCEL' && invoiceItems.length > 0) {
|
||||
html += '<hr style="margin: 10px 0;">';
|
||||
html += '<div class="ifnex-price-row"><span>📋 تعداد اقلام:</span><strong>' + invoiceItems.length + '</strong></div>';
|
||||
var invoiceTotal = 0;
|
||||
invoiceItems.forEach(function(item) {
|
||||
invoiceTotal += calculateItemTotal(item.id);
|
||||
});
|
||||
html += '<div class="ifnex-price-row"><span>💰 مجموع فاکتور:</span><strong>$' + numberFormat(invoiceTotal) + ' USD</strong></div>';
|
||||
}
|
||||
|
||||
html += '<hr style="margin: 10px 0;">';
|
||||
html += '<div class="ifnex-price-row"><span>قیمت پایه:</span><strong>' + (data.base_price || '0') + ' درهم</strong></div>';
|
||||
html += '<div class="ifnex-price-row"><span>ناخالص:</span><strong>' + (data.net_dirham || '0') + ' درهم</strong></div>';
|
||||
@ -489,5 +748,8 @@ jQuery(document).ready(function($) {
|
||||
loadCountries();
|
||||
// افزودن بسته اول بهصورت پیشفرض
|
||||
addPackage();
|
||||
// بررسی نوع محموله برای نمایش/مخفیکردن Step Invoice
|
||||
// این تابع خودش اگه PARCEL باشه و اقلام خالی باشه، یه قلم اضافه میکنه
|
||||
updateInvoiceStepVisibility();
|
||||
}
|
||||
});
|
||||
|
||||
@ -497,8 +497,9 @@ function ifnex_order_form_shortcode($atts) {
|
||||
<div class="ifnex-steps">
|
||||
<div class="ifnex-step active" data-step="1"><span>۱</span><br>مسیر و بسته</div>
|
||||
<div class="ifnex-step" data-step="2"><span>۲</span><br>اطلاعات تماس</div>
|
||||
<div class="ifnex-step" data-step="3"><span>۳</span><br>بررسی قیمت</div>
|
||||
<div class="ifnex-step" data-step="4"><span>۴</span><br>تأیید نهایی</div>
|
||||
<div class="ifnex-step ifnex-step-invoice" data-step="3" style="display:none;"><span>۳</span><br>اقلام گمرکی</div>
|
||||
<div class="ifnex-step ifnex-step-discount" data-step="4"><span>۴</span><br>بررسی قیمت</div>
|
||||
<div class="ifnex-step" data-step="5"><span>۵</span><br>تأیید نهایی</div>
|
||||
</div>
|
||||
|
||||
<div id="ifnex-form-error" class="ifnex-error-box" style="display:none;"></div>
|
||||
@ -593,8 +594,43 @@ function ifnex_order_form_shortcode($atts) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Review & Discount -->
|
||||
<div class="ifnex-form-step" data-step="3">
|
||||
<!-- Step 3: Invoice Items (فقط برای PARCEL) -->
|
||||
<div class="ifnex-form-step ifnex-step-invoice-content" data-step="3" style="display:none;">
|
||||
<div class="ifnex-form-card">
|
||||
<h3>📋 اقلام گمرکی (Invoice)</h3>
|
||||
<p style="font-size: 12px; color: #666; margin-bottom: 12px;">
|
||||
اقلام موجود در بستههای خود را وارد کنید. این اطلاعات برای فاکتور صادراتی الزامی است. حداکثر ۹ ردیف.
|
||||
</p>
|
||||
|
||||
<div id="ifnex-items-container">
|
||||
<!-- آیتمها اینجا اضافه میشن -->
|
||||
</div>
|
||||
|
||||
<button type="button" id="ifnex-add-item" class="ifnex-btn ifnex-btn-info" style="margin-top: 10px;">
|
||||
➕ افزودن قلم دیگر
|
||||
</button>
|
||||
|
||||
<!-- خلاصه فاکتور -->
|
||||
<div class="ifnex-items-summary" id="ifnex-items-summary" style="display:none; margin-top: 16px; padding: 12px; background: #fef3c7; border: 1px solid #fde68a; border-radius: 6px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div>
|
||||
<strong>📦 تعداد اقلام:</strong> <span id="ifnex-item-count">0</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>💰 مجموع فاکتور (USD):</strong> <span id="ifnex-invoice-total" style="color: #059669; font-size: 16px;">0.00</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ifnex-form-nav">
|
||||
<button class="ifnex-btn ifnex-prev-step">→ قبلی</button>
|
||||
<button class="ifnex-btn ifnex-btn-primary ifnex-next-step">ادامه ←</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4: Review & Discount -->
|
||||
<div class="ifnex-form-step" data-step="4">
|
||||
<div class="ifnex-form-card">
|
||||
<h3>🏷️ کد تخفیف (اختیاری)</h3>
|
||||
<div class="ifnex-form-grid">
|
||||
@ -611,8 +647,8 @@ function ifnex_order_form_shortcode($atts) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4: Confirm -->
|
||||
<div class="ifnex-form-step" data-step="4">
|
||||
<!-- Step 5: Confirm -->
|
||||
<div class="ifnex-form-step" data-step="5">
|
||||
<div class="ifnex-form-card">
|
||||
<h3>✅ تأیید نهایی سفارش</h3>
|
||||
<div class="ifnex-price-summary" id="ifnex-price-summary"></div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user