Merge branch 'dillon/137-project-upgrade-update-1A' into vapor/test

This commit is contained in:
Dillon Ngo
2026-06-28 01:20:22 +08:00
16 changed files with 108 additions and 59 deletions
@@ -146,14 +146,19 @@ class CreateBookingRefundLogic extends AbstractControllerLogic
$service_charges_to_refund = $isFullyRefund ? $quotationBeforeCurrentRefund->getServiceCharge() : $quotationBeforeCurrentRefund->getServiceCharge() - $quotationAfterRefund->getServiceCharge();
}
// refund service charges if booking is not E2E
$refundTotal = $refundAmount;
if ($booking->service_id !== 5) {
$refundTotal = $refundTotal + $service_charges_to_refund + $transaction->tax;
} else {
$service_charges_to_refund = 0;
}
// refund service charges if booking is not E2E - starts
// if ($booking->service_id !== 5) {
// $refundTotal = $refundTotal + $service_charges_to_refund + $transaction->tax;
// } else {
// $service_charges_to_refund = 0;
// }
// refund service charges if booking is not E2E - ends
//Reinstate refund service charges to all services, no checking if it is E2E or otherwise
$refundTotal = $refundTotal + $service_charges_to_refund + $transaction->tax;
$object = new TransactionObject($billNumber, TransactionType::REFUND, 1, $booking->company->id,
1, PaymentMethodType::CASH,
@@ -133,10 +133,10 @@ class RegenerateInvoiceBookingV2Processor
}
//for E-INVOICE
else {
$transaction = $booking->transactions()->whereIn('type', [TransactionType::SUPPLIER_DELIVER])->get();
foreach ($transaction as $key => $row) {
$this->deletesTransaction->execute($row);
}
// $transaction = $booking->transactions()->whereIn('type', [TransactionType::SUPPLIER_DELIVER])->get();
// foreach ($transaction as $key => $row) {
// $this->deletesTransaction->execute($row);
// }
}
$document = $booking->documents()->whereIn('document_type', [DocumentType::PURCHASE_ORDER, DocumentType::INVOICE, DocumentType::EINVOICE, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->get();
@@ -347,24 +347,32 @@ class CreateInvoiceTransactionV2Processor
}
if($transactionTypeBill){
$transaction_object = new TransactionObject(
$billNumber,
TransactionType::SUPPLIER_DELIVER,
$transactionTypeBill->issuer,
$transactionTypeBill->receiver,
$transactionTypeBill->recipient_bank_account_id,
$transactionTypeBill->payment_method,
$payable_amount,
$booking_amount,
$transactionTypeBill->currency_id,
$transactionTypeBill->original_currency_id,
$booking_currency_average_rate,
$total_tax,
$total_service_charge,
null,
ApprovalStatus::APPROVED
);
$supplier_deliver_order_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object);
$supplier_deliver_order_transaction = $booking->transactions()
->whereIn('type', [TransactionType::SUPPLIER_DELIVER])
->complete()
->latest()
->first();
if(!$supplier_deliver_order_transaction){
$transaction_object = new TransactionObject(
$billNumber,
TransactionType::SUPPLIER_DELIVER,
$transactionTypeBill->issuer,
$transactionTypeBill->receiver,
$transactionTypeBill->recipient_bank_account_id,
$transactionTypeBill->payment_method,
$payable_amount,
$booking_amount,
$transactionTypeBill->currency_id,
$transactionTypeBill->original_currency_id,
$booking_currency_average_rate,
$total_tax,
$total_service_charge,
null,
ApprovalStatus::APPROVED
);
$supplier_deliver_order_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object);
}
// supply deliver order
$this->invoiceDocumentProcessor->execute($supplier_deliver_order_transaction, $purchaseOrder, $supplier, DocumentType::SUPPLIER_DELIVER_ORDER, null);
@@ -201,7 +201,7 @@ const rules = computed(() => ({
},
password_confirmation: {
required: requiredIf(step.value === 2),
sameAs: sameAs(parameters.password)
sameAs: sameAs(parameters.password, 'password')
}
}
}));
@@ -126,6 +126,7 @@ const submitForm = () => {
successHandler: (response: any) => {
isLoading.value = false;
if (response.payload.data.payment_method === 5) {
closeModal();
window.location.href = route('billplz.bill', response.payload.data.payment_reference) + '?auto_submit=true';
} else {
emit('cancelQuotation');
@@ -12,7 +12,7 @@
<div class="col">
<validation-wrapper-component :validator="v$.fix_amount">
<label class="muted">Booking Amount ({{data.fixed_currency.short_code}})</label>
<input type="text" class="form-control" v-model="fix_amount" v-money3="money">
<input type="text" class="form-control" v-model.lazy="fix_amount" v-money3="money">
</validation-wrapper-component>
</div>
</div>
@@ -13,7 +13,7 @@
<div class="col">
<validation-wrapper-component :validator="v$.amount_to_edit">
<label class="muted">Booking Amount ({{data.fixed_currency.short_code}})</label>
<input type="text" class="form-control" v-model="amount_to_edit" v-money3="money">
<input type="text" class="form-control" v-model.lazy="amount_to_edit" v-money3="money">
</validation-wrapper-component>
</div>
</div>
@@ -84,6 +84,7 @@ const submitForm = async () => {
successHandler: () => {
closeModal();
formHandler();
queueStore.reloadList({ name: "bookingDetailSection" });
}
});
};
@@ -85,6 +85,7 @@ const submitForm = async () => {
successHandler: () => {
closeModal();
formHandler();
queueStore.reloadList({ name: "bookingDetailSection" });
},
errorHandler: (response: any) => {
formHandler(response.message);
@@ -15,7 +15,7 @@
<div class="btn btn-sm btn-success btn-block b-rad-none" data-dismiss="modal">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-sm btn-danger btn-block b-rad-none" @click="submit(route('api.transaction.bill.status', item.id, 'pending'), 'put', section, true, true)">Confirm</div>
<div class="btn btn-sm btn-danger btn-block b-rad-none" @click="submit(route('api.transaction.bill.status', item.id, 'pending'), 'put', section, true, true, null, { successHandler })">Confirm</div>
</div>
</div>
</div>
@@ -27,10 +27,13 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useUiStore } from '@/stores/ui';
import { useRequest } from '@/composables/useRequest';
import { useFormHandler } from '@/composables/useFormHandler';
import { useQueueStore } from '@/stores/queue';
import { useComponentHandler } from '@/composables/useComponentHandler';
import route from '@/general/functions/router';
const queueStore = useQueueStore();
const props = defineProps<{
data: any;
section: string;
@@ -38,6 +41,12 @@ const props = defineProps<{
const uiStore = useUiStore();
const isLoading = computed(() => uiStore.isLoading(props.section));
const { submit } = useRequest();
const { submit, closeModal } = useFormHandler(props);
const { item } = useComponentHandler(props);
const successHandler = (response: any) => {
closeModal();
queueStore.reloadList({ name: "bookingDetailSection" });
};
</script>
@@ -9,7 +9,7 @@
<div class="col">
<div class="row m-b-5">
<div class="col p-r-0">
<div class="btn btn-xs btn-outline-primary btn-block text-left b-rad-none p-t-0 p-b-0 p-l-15 p-r-15" @click="selectedSupplier.status = !selectedSupplier.status">
<div class="btn btn-xs btn-outline-primary btn-block text-left b-rad-none p-t-0 p-b-0 p-l-15 p-r-15" :class="{'disabled': sectionLoading}" @click="!sectionLoading && (dropdownOpen = !dropdownOpen)">
<div class="row">
<div class="col p-t-5 p-b-5 fs-9">
{{selectedSupplier.name}} - {{selectedSupplier.reference}}
@@ -17,14 +17,14 @@
<div class="col-auto b-l b-success">
<div class="row h-100 align-items-center">
<div class="col">
<i class="fa" :class="[{'fa-angle-down': !selectedSupplier.status}, {'fa-angle-up': selectedSupplier.status}]"></i>
<i class="fa" :class="[{'fa-angle-down': !dropdownOpen}, {'fa-angle-up': dropdownOpen}]"></i>
</div>
</div>
</div>
</div>
</div>
<div class="relative w-100">
<div class="absolute w-100 b-l b-b b-r b-primary" :class="[{'hide': !selectedSupplier.status}]" style="top: 100%; right: 0; z-index: 1;">
<div class="absolute w-100 b-l b-b b-r b-primary" :class="[{'hide': !dropdownOpen}]" style="top: 100%; right: 0; z-index: 1;">
<div class="row text-left no-margin bg-white">
<div class="col no-padding">
<div class="row no-margin" v-for="supplier in suppliers" :key="supplier.id" :data="supplier">
@@ -70,17 +70,21 @@
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ref, computed, onMounted } from 'vue';
import route from '@/general/functions/router';
import { useUiStore } from '@/stores/ui';
import { useRequest } from '@/composables/useRequest';
const { submit } = useRequest();
const uiStore = useUiStore();
const sectionLoading = computed(() => uiStore.isLoading('transactionGroupsListPaymentSection'));
const suppliers = ref<any[]>([]);
const dropdownOpen = ref(false);
const selectedSupplier = ref<any>({
id: '',
name: '',
status: false
name: ''
});
const payments = ref<any[]>([]);
const currencyOrderKey = ref(1);
@@ -88,21 +92,22 @@ const currencyOrderKey = ref(1);
const successHandler = (response: any) => {
suppliers.value = response.payload.data;
if (suppliers.value.length > 0) {
selectedSupplier.value = { ...suppliers.value[0], status: false };
selectedSupplier.value = { ...suppliers.value[0] };
}
updateList();
};
const updateSupplier = (supplier: any) => {
if (supplier !== selectedSupplier.value) {
selectedSupplier.value = { ...supplier, status: false };
dropdownOpen.value = false;
if (supplier.id !== selectedSupplier.value.id) {
selectedSupplier.value = { ...supplier };
updateList();
}
};
const updateList = () => {
currencyOrderKey.value++;
selectedSupplier.value.status = false;
dropdownOpen.value = false;
payments.value = [];
};
@@ -18,6 +18,7 @@ import { required } from '@vuelidate/validators';
import route from '@/general/functions/router';
interface Props {
section: string;
data: any;
}
@@ -27,11 +28,7 @@ const emit = defineEmits<{
(e: 'submit'): void;
}>();
const { parameters, submit } = useFormHandler({
data: {
name: props.data?.name || ''
}
});
const { parameters, submit } = useFormHandler(props);
const rules = {
parameters: {
@@ -1,6 +1,6 @@
<template>
<div class="row d-none" :class="[{'d-flex': authStore.isAdmin}]" v-if="authStore.isAdmin && company" >
<div class="col-8">
<div class="row d-flex" :class="[{'d-flex': authStore.isAdmin}]" v-if="authStore.isAdmin && company" >
<div class="col-md-8">
<div class="row m-b-20">
<div class="col bg-white p-t-10 p-b-15 p-r-20 shadow-sm">
<customer-profile-component :data="company"></customer-profile-component>
@@ -12,7 +12,7 @@
<div class="col">
<div class="row tabsContainer">
<div class="col">
<div class="row m-l-0 m-r-0 d-none d-md-flex">
<div class="row m-l-0 m-r-0 d-flex flex-nowrap overflow-auto">
<div class="col-7">
<div class="row">
<div class="col m-r-5">
@@ -2,7 +2,7 @@
<div v-if="validator?.$error" class="text-danger fs-10">
<template v-for="(rule, name) in validator" :key="name">
<small class="bold" v-if="typeof rule === 'object' && rule.$invalid">
<span class="btn-block">{{ getErrorMessage(name) }}</span>
<span class="btn-block">{{ getErrorMessage({ ...rule, $validator: name as unknown as string }) }}</span>
</small>
</template>
</div>
@@ -15,6 +15,12 @@ interface Props {
const props = defineProps<Props>();
interface ValidationErrorItem {
$validator: string;
$invalid: boolean;
$params?: Record<string, any>;
}
const errorMessages: Record<string, string> = {
required: 'this field is required',
email: 'enter a valid email address',
@@ -28,7 +34,23 @@ const errorMessages: Record<string, string> = {
numeric: 'this field must be numeric'
};
const getErrorMessage = (name: string | number) => {
return errorMessages[name as string] || 'invalid field';
const getErrorMessage = (error: ValidationErrorItem): string => {
if (!error) return 'invalid value';
const validatorName = error.$validator;
const params = error.$params || {};
if (validatorName === 'minLength') return `${errorMessages.minLength} ${params.min} characters`;
if (validatorName === 'maxLength') return `${errorMessages.maxLength} ${params.max} characters`;
if (validatorName === 'sameAs' || validatorName === 'sameAsPassword') {
let fieldName = params.otherName || params.eq || 'password';
if (fieldName === 'password') fieldName = 'password';
return `${errorMessages.sameAs} ${fieldName} field`;
}
if (validatorName === 'minValue') return `${errorMessages.minValue} ${params.min}`;
if (validatorName === 'maxValue') return `${errorMessages.maxValue} ${params.max}`;
return errorMessages[validatorName] || 'invalid value';
};
</script>
@@ -111,7 +111,7 @@ const rules = computed(() => ({
password: { required, minLength: minLength(6) },
password_confirmation: {
required,
sameAsPassword: sameAs(() => parameters.value.password)
sameAsPassword: sameAs(() => parameters.value.password, 'password')
},
is_internal: {}
}
@@ -105,7 +105,7 @@ const rules = computed(() => ({
password: { required, minLength: minLength(6) },
password_confirmation: {
required,
sameAsPassword: sameAs(passwordRef)
sameAsPassword: sameAs(passwordRef, 'password')
}
}
}));
@@ -90,7 +90,7 @@ import { useComponentHandler } from '@/composables/useComponentHandler';
import { useFormHandler } from '@/composables/useFormHandler';
import { useAuthStore } from '@/stores/auth';
import { useVuelidate } from '@vuelidate/core';
import { required, requiredIf, minValue } from '@vuelidate/validators';
import { required, requiredIf } from '@vuelidate/validators';
import route from '@/general/functions/router';
interface Props {
@@ -132,7 +132,7 @@ const rules = computed(() => ({
parameters: {
amount: {
required,
minValue: minValue(minAllowedAmount.value)
minValue: 0
},
reference: {
required: requiredIf(() => parameters.value.transaction_type !== 0)