mirror of
https://github.com/frappe/books.git
synced 2024-12-22 10:58:59 +00:00
fix: refactor code
This commit is contained in:
parent
28d1d8e915
commit
7b5e4562ec
@ -9,22 +9,12 @@ export type ItemSerialNumbers = { [item: string]: string };
|
||||
export type DiscountType = 'percent' | 'amount';
|
||||
|
||||
export type ModalName =
|
||||
| 'ShiftOpen'
|
||||
| 'ShiftClose'
|
||||
| 'Payment'
|
||||
| 'LoyaltyProgram'
|
||||
| 'SavedInvoice'
|
||||
| 'RouteToInvoiceList'
|
||||
| 'CouponCode';
|
||||
|
||||
export type ModernPosModalName =
|
||||
| 'Keyboard'
|
||||
| 'ShiftOpen'
|
||||
| 'ShiftClose'
|
||||
| 'Payment'
|
||||
| 'ShiftClose'
|
||||
| 'LoyaltyProgram'
|
||||
| 'SavedInvoice'
|
||||
| 'RouteToInvoiceList'
|
||||
| 'Alert'
|
||||
| 'CouponCode';
|
||||
|
||||
export interface POSItem {
|
||||
|
@ -10,7 +10,7 @@
|
||||
<div class="flex col-span-2 gap-5">
|
||||
<Button
|
||||
class="py-5 w-full bg-red-500 dark:bg-red-700"
|
||||
@click="$emit('toggleModal', 'RouteToInvoiceList')"
|
||||
@click="$emit('toggleModal', 'Alert')"
|
||||
>
|
||||
<slot>
|
||||
<p class="uppercase text-lg text-white font-semibold">
|
||||
@ -23,7 +23,7 @@
|
||||
class="w-full py-5 bg-green-500 dark:bg-green-700"
|
||||
@click="
|
||||
routeTo('/list/SalesInvoice');
|
||||
$emit('toggleModal', 'RouteToInvoiceList');
|
||||
$emit('toggleModal', 'Alert');
|
||||
"
|
||||
>
|
||||
<slot>
|
||||
|
@ -15,37 +15,34 @@
|
||||
:open-modal="openLoyaltyProgramModal"
|
||||
:loyalty-points="loyaltyPoints"
|
||||
:loyalty-program="loyaltyProgram"
|
||||
@set-loyalty-points="setLoyaltyPoints"
|
||||
@set-loyalty-points="emitSetLoyaltyPoints"
|
||||
@toggle-modal="toggleModal"
|
||||
/>
|
||||
<SavedInvoiceModal
|
||||
:open-modal="openSavedInvoiceModal"
|
||||
:modal-status="openSavedInvoiceModal"
|
||||
@selected-invoice-name="selectedInvoiceName"
|
||||
@selected-invoice-name="emitSelectedInvoice"
|
||||
@toggle-modal="toggleModal"
|
||||
/>
|
||||
|
||||
<CouponCodeModal
|
||||
:open-modal="openCouponCodeModal"
|
||||
@set-coupons-count="setCouponsCount"
|
||||
@toggle-modal="toggleModal"
|
||||
@set-coupons-count="emitCouponsCount"
|
||||
/>
|
||||
|
||||
<PaymentModal
|
||||
:open-modal="openPaymentModal"
|
||||
@create-transaction="createTransaction"
|
||||
@toggle-modal="toggleModal"
|
||||
@set-cash-amount="setCashAmount"
|
||||
@set-transfer-amount="setTransferAmount"
|
||||
@set-cash-amount="emitSetCashAmount"
|
||||
@set-coupons-count="emitCouponsCount"
|
||||
@set-transfer-ref-no="setTransferRefNo"
|
||||
@set-coupons-count="setCouponsCount"
|
||||
@set-transfer-amount="emitSetTransferAmount"
|
||||
@create-transaction="emitCreateTransaction"
|
||||
@set-transfer-clearance-date="setTransferClearanceDate"
|
||||
/>
|
||||
|
||||
<AlertModal
|
||||
:open-modal="openRouteToInvoiceListModal"
|
||||
@toggle-modal="toggleModal"
|
||||
/>
|
||||
<AlertModal :open-modal="openAlertModal" @toggle-modal="toggleModal" />
|
||||
|
||||
<div
|
||||
class="bg-gray-25 dark:bg-gray-875 gap-2 grid grid-cols-12 p-4"
|
||||
@ -79,7 +76,7 @@
|
||||
:border="true"
|
||||
:value="itemSearchTerm"
|
||||
@keyup.enter="
|
||||
async () => await addItem(await getItem(itemSearchTerm))
|
||||
async () => await selectItem(await getItem(itemSearchTerm))
|
||||
"
|
||||
@change="(item: string) =>itemSearchTerm= item"
|
||||
/>
|
||||
@ -89,7 +86,7 @@
|
||||
class="w-1/3"
|
||||
@item-selected="
|
||||
async (name: string) => {
|
||||
await addItem(await getItem(name));
|
||||
await selectItem(await getItem(name));
|
||||
}
|
||||
"
|
||||
/>
|
||||
@ -98,15 +95,15 @@
|
||||
<ItemsTable
|
||||
v-if="tableView"
|
||||
:items="items"
|
||||
:item-qty-map="itemQtyMap"
|
||||
@add-item="addItem"
|
||||
:item-qty-map="itemQuantityMap as ItemQtyMap"
|
||||
@add-item="selectItem"
|
||||
/>
|
||||
|
||||
<ItemsGrid
|
||||
v-else
|
||||
:items="items"
|
||||
:item-qty-map="itemQtyMap"
|
||||
@add-item="addItem"
|
||||
:item-qty-map="itemQuantityMap as ItemQtyMap"
|
||||
@add-item="selectItem"
|
||||
/>
|
||||
|
||||
<div class="flex fixed bottom-0 p-1 mb-7 gap-x-3">
|
||||
@ -145,7 +142,7 @@
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="px-1.5 py-1 rounded-md bg-gray-100"
|
||||
@click="routeToSinvList"
|
||||
@click="() => $emit('routeToSinvList')"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@ -191,10 +188,12 @@
|
||||
hidden: !fyo.singles.AccountingSettings?.enableLoyaltyProgram,
|
||||
'bg-gray-100': loyaltyPoints,
|
||||
'dark:bg-gray-600 cursor-not-allowed':
|
||||
!loyaltyPoints || !sinvDoc.party || !sinvDoc.items?.length,
|
||||
!loyaltyPoints ||
|
||||
!sinvDoc?.party ||
|
||||
!sinvDoc?.items?.length,
|
||||
}"
|
||||
@click="
|
||||
loyaltyPoints && sinvDoc.party && sinvDoc.items?.length
|
||||
loyaltyPoints && sinvDoc?.party && sinvDoc?.items?.length
|
||||
? toggleModal('LoyaltyProgram', true)
|
||||
: null
|
||||
"
|
||||
@ -244,7 +243,7 @@
|
||||
hidden: !fyo.singles.AccountingSettings?.enableCouponCode,
|
||||
'bg-gray-100': loyaltyPoints,
|
||||
'dark:bg-gray-600 cursor-not-allowed':
|
||||
!sinvDoc.party || !sinvDoc.items?.length,
|
||||
!sinvDoc?.party || !sinvDoc?.items?.length,
|
||||
}"
|
||||
@click="openCouponModal()"
|
||||
>
|
||||
@ -372,13 +371,13 @@
|
||||
>
|
||||
<!-- Customer Search -->
|
||||
<MultiLabelLink
|
||||
v-if="sinvDoc.fieldMap"
|
||||
v-if="sinvDoc?.fieldMap"
|
||||
class="flex-shrink-0"
|
||||
secondary-link="phone"
|
||||
:border="true"
|
||||
:value="sinvDoc.party"
|
||||
:df="sinvDoc.fieldMap.party"
|
||||
@change="(value:string) => setCustomer(value)"
|
||||
:value="sinvDoc?.party"
|
||||
:df="sinvDoc?.fieldMap.party"
|
||||
@change="(value:string) => $emit('setCustomer',value)"
|
||||
/>
|
||||
|
||||
<SelectedItemTable />
|
||||
@ -439,10 +438,10 @@
|
||||
:text-right="true"
|
||||
/>
|
||||
<FloatingLabelCurrencyInput
|
||||
v-if="sinvDoc.fieldMap"
|
||||
:df="sinvDoc.fieldMap.grandTotal"
|
||||
v-if="sinvDoc?.fieldMap"
|
||||
:df="sinvDoc?.fieldMap.grandTotal"
|
||||
size="large"
|
||||
:value="sinvDoc.grandTotal"
|
||||
:value="sinvDoc?.grandTotal"
|
||||
:read-only="true"
|
||||
:text-right="true"
|
||||
/>
|
||||
@ -452,8 +451,8 @@
|
||||
<div class="w-full">
|
||||
<Button
|
||||
class="w-full bg-violet-500 dark:bg-violet-700 py-6"
|
||||
:disabled="!sinvDoc.party || !sinvDoc.items?.length"
|
||||
@click="handleSaveInvoiceAction"
|
||||
:disabled="!sinvDoc?.party || !sinvDoc?.items?.length"
|
||||
@click="$emit('saveInvoiceAction')"
|
||||
>
|
||||
<slot>
|
||||
<p class="uppercase text-lg text-white font-semibold">
|
||||
@ -475,8 +474,8 @@
|
||||
<div class="w-full">
|
||||
<Button
|
||||
class="w-full bg-red-500 dark:bg-red-700 py-6"
|
||||
:disabled="!sinvDoc.items?.length"
|
||||
@click="clearValues"
|
||||
:disabled="!sinvDoc?.items?.length"
|
||||
@click="() => $emit('clearValues')"
|
||||
>
|
||||
<slot>
|
||||
<p class="uppercase text-lg text-white font-semibold">
|
||||
@ -507,600 +506,171 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Button from 'src/components/Button.vue';
|
||||
import ClosePOSShiftModal from './ClosePOSShiftModal.vue';
|
||||
import FloatingLabelCurrencyInput from 'src/components/POS/FloatingLabelCurrencyInput.vue';
|
||||
import FloatingLabelFloatInput from 'src/components/POS/FloatingLabelFloatInput.vue';
|
||||
import ItemsTable from 'src/components/POS/Classic/ItemsTable.vue';
|
||||
import Link from 'src/components/Controls/Link.vue';
|
||||
import OpenPOSShiftModal from './OpenPOSShiftModal.vue';
|
||||
import PaymentModal from './PaymentModal.vue';
|
||||
import SelectedItemTable from 'src/components/POS/Classic/SelectedItemTable.vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { Money } from 'pesa';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { routeTo, toggleSidebar } from 'src/utils/ui';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
|
||||
import { routeTo } from 'src/utils/ui';
|
||||
import { getItem } from 'src/utils/pos';
|
||||
import AlertModal from './AlertModal.vue';
|
||||
import PaymentModal from './PaymentModal.vue';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import { Item } from 'models/baseModels/Item/Item';
|
||||
import Link from 'src/components/Controls/Link.vue';
|
||||
import CouponCodeModal from './CouponCodeModal.vue';
|
||||
import { ModalName } from 'src/components/POS/types';
|
||||
import SavedInvoiceModal from './SavedInvoiceModal.vue';
|
||||
import OpenPOSShiftModal from './OpenPOSShiftModal.vue';
|
||||
import ClosePOSShiftModal from './ClosePOSShiftModal.vue';
|
||||
import Barcode from 'src/components/Controls/Barcode.vue';
|
||||
import { Payment } from 'models/baseModels/Payment/Payment';
|
||||
import LoyaltyProgramModal from './LoyaltyprogramModal.vue';
|
||||
import ItemsGrid from 'src/components/POS/Classic/ItemsGrid.vue';
|
||||
import { t } from 'fyo';
|
||||
import ItemsTable from 'src/components/POS/Classic/ItemsTable.vue';
|
||||
import MultiLabelLink from 'src/components/Controls/MultiLabelLink.vue';
|
||||
import { InvoiceItem } from 'models/baseModels/InvoiceItem/InvoiceItem';
|
||||
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
|
||||
import SelectedItemTable from 'src/components/POS/Classic/SelectedItemTable.vue';
|
||||
import FloatingLabelFloatInput from 'src/components/POS/FloatingLabelFloatInput.vue';
|
||||
import FloatingLabelCurrencyInput from 'src/components/POS/FloatingLabelCurrencyInput.vue';
|
||||
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
||||
import {
|
||||
ItemQtyMap,
|
||||
ItemSerialNumbers,
|
||||
POSItem,
|
||||
} from 'src/components/POS/types';
|
||||
import { Item } from 'models/baseModels/Item/Item';
|
||||
import { ModalName } from 'src/components/POS/types';
|
||||
import { Money } from 'pesa';
|
||||
import { Payment } from 'models/baseModels/Payment/Payment';
|
||||
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
|
||||
import { Shipment } from 'models/inventory/Shipment';
|
||||
import { showToast } from 'src/utils/interactive';
|
||||
import {
|
||||
getItem,
|
||||
getItemDiscounts,
|
||||
getItemQtyMap,
|
||||
getTotalQuantity,
|
||||
getTotalTaxedAmount,
|
||||
validateIsPosSettingsSet,
|
||||
validateShipment,
|
||||
validateSinv,
|
||||
} from 'src/utils/pos';
|
||||
import Barcode from 'src/components/Controls/Barcode.vue';
|
||||
import {
|
||||
getAddedLPWithGrandTotal,
|
||||
getPricingRule,
|
||||
removeFreeItems,
|
||||
} from 'models/helpers';
|
||||
import LoyaltyProgramModal from './LoyaltyprogramModal.vue';
|
||||
import AlertModal from './AlertModal.vue';
|
||||
import SavedInvoiceModal from './SavedInvoiceModal.vue';
|
||||
import CouponCodeModal from './CouponCodeModal.vue';
|
||||
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
||||
import MultiLabelLink from 'src/components/Controls/MultiLabelLink.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ClassicPOS',
|
||||
components: {
|
||||
Button,
|
||||
ClosePOSShiftModal,
|
||||
FloatingLabelCurrencyInput,
|
||||
FloatingLabelFloatInput,
|
||||
ItemsTable,
|
||||
ItemsGrid,
|
||||
Link,
|
||||
MultiLabelLink,
|
||||
AlertModal,
|
||||
OpenPOSShiftModal,
|
||||
PaymentModal,
|
||||
LoyaltyProgramModal,
|
||||
SavedInvoiceModal,
|
||||
CouponCodeModal,
|
||||
SelectedItemTable,
|
||||
Button,
|
||||
Barcode,
|
||||
ItemsGrid,
|
||||
AlertModal,
|
||||
ItemsTable,
|
||||
PaymentModal,
|
||||
MultiLabelLink,
|
||||
CouponCodeModal,
|
||||
OpenPOSShiftModal,
|
||||
SavedInvoiceModal,
|
||||
SelectedItemTable,
|
||||
ClosePOSShiftModal,
|
||||
LoyaltyProgramModal,
|
||||
FloatingLabelFloatInput,
|
||||
FloatingLabelCurrencyInput,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
cashAmount: computed(() => this.cashAmount),
|
||||
doc: computed(() => this.sinvDoc),
|
||||
isDiscountingEnabled: computed(() => this.isDiscountingEnabled),
|
||||
itemDiscounts: computed(() => this.itemDiscounts),
|
||||
itemQtyMap: computed(() => this.itemQtyMap),
|
||||
itemSerialNumbers: computed(() => this.itemSerialNumbers),
|
||||
sinvDoc: computed(() => this.sinvDoc),
|
||||
appliedCoupons: computed(() => this.sinvDoc.coupons),
|
||||
coupons: computed(() => this.coupons),
|
||||
totalTaxedAmount: computed(() => this.totalTaxedAmount),
|
||||
transferAmount: computed(() => this.transferAmount),
|
||||
transferClearanceDate: computed(() => this.transferClearanceDate),
|
||||
transferRefNo: computed(() => this.transferRefNo),
|
||||
};
|
||||
props: {
|
||||
cashAmount: Money,
|
||||
itemDiscounts: Money,
|
||||
openAlertModal: Boolean,
|
||||
disablePayButton: Boolean,
|
||||
openPaymentModal: Boolean,
|
||||
openCouponCodeModal: Boolean,
|
||||
openShiftCloseModal: Boolean,
|
||||
openSavedInvoiceModal: Boolean,
|
||||
openLoyaltyProgramModal: Boolean,
|
||||
openAppliedCouponsModal: Boolean,
|
||||
loyaltyPoints: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
loyaltyProgram: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
appliedCouponsCount: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
sinvDoc: {
|
||||
type: Object as PropType<SalesInvoice | undefined>,
|
||||
default: undefined,
|
||||
},
|
||||
itemQuantityMap: {
|
||||
type: Object as PropType<ItemQtyMap>,
|
||||
default: () => ({}),
|
||||
},
|
||||
coupons: {
|
||||
type: Object as PropType<AppliedCouponCodes>,
|
||||
default: () => ({}),
|
||||
},
|
||||
items: {
|
||||
type: Array as PropType<POSItem[] | undefined>,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
'addItem',
|
||||
'toggleModal',
|
||||
'setCustomer',
|
||||
'clearValues',
|
||||
'setCashAmount',
|
||||
'setCouponsCount',
|
||||
'routeToSinvList',
|
||||
'setLoyaltyPoints',
|
||||
'saveInvoiceAction',
|
||||
'createTransaction',
|
||||
'setTransferAmount',
|
||||
'selectedInvoiceName',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
items: [] as POSItem[],
|
||||
|
||||
tableView: true,
|
||||
|
||||
isItemsSeeded: false,
|
||||
openPaymentModal: false,
|
||||
openLoyaltyProgramModal: false,
|
||||
openSavedInvoiceModal: false,
|
||||
openCouponCodeModal: false,
|
||||
openAppliedCouponsModal: false,
|
||||
openShiftCloseModal: false,
|
||||
openShiftOpenModal: false,
|
||||
openRouteToInvoiceListModal: false,
|
||||
|
||||
additionalDiscounts: fyo.pesa(0),
|
||||
cashAmount: fyo.pesa(0),
|
||||
itemDiscounts: fyo.pesa(0),
|
||||
totalTaxedAmount: fyo.pesa(0),
|
||||
transferAmount: fyo.pesa(0),
|
||||
|
||||
totalQuantity: 0,
|
||||
totalTaxedAmount: fyo.pesa(0),
|
||||
additionalDiscounts: fyo.pesa(0),
|
||||
|
||||
loyaltyPoints: 0,
|
||||
appliedLoyaltyPoints: 0,
|
||||
loyaltyProgram: '' as string,
|
||||
paymentDoc: {} as Payment,
|
||||
itemSerialNumbers: {} as ItemSerialNumbers,
|
||||
|
||||
appliedCoupons: [] as AppliedCouponCodes[],
|
||||
appliedCouponsCount: 0,
|
||||
|
||||
defaultCustomer: undefined as string | undefined,
|
||||
itemSearchTerm: '',
|
||||
transferRefNo: undefined as string | undefined,
|
||||
|
||||
transferClearanceDate: undefined as Date | undefined,
|
||||
|
||||
itemQtyMap: {} as ItemQtyMap,
|
||||
itemSerialNumbers: {} as ItemSerialNumbers,
|
||||
paymentDoc: {} as Payment,
|
||||
sinvDoc: {} as SalesInvoice,
|
||||
coupons: {} as AppliedCouponCodes,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
defaultPOSCashAccount: () =>
|
||||
fyo.singles.POSSettings?.cashAccount ?? undefined,
|
||||
isDiscountingEnabled(): boolean {
|
||||
return !!fyo.singles.AccountingSettings?.enableDiscounting;
|
||||
},
|
||||
isPosShiftOpen: () => !!fyo.singles.POSShift?.isShiftOpen,
|
||||
isPaymentAmountSet(): boolean {
|
||||
if (this.sinvDoc.grandTotal?.isZero()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.cashAmount.isZero() && this.transferAmount.isZero()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
disablePayButton(): boolean {
|
||||
if (!this.sinvDoc.items?.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.sinvDoc.party) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
sinvDoc: {
|
||||
handler() {
|
||||
this.updateValues();
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
await this.setItems();
|
||||
},
|
||||
async activated() {
|
||||
toggleSidebar(false);
|
||||
validateIsPosSettingsSet(fyo);
|
||||
this.setCouponCodeDoc();
|
||||
this.setSinvDoc();
|
||||
this.setDefaultCustomer();
|
||||
await this.setItemQtyMap();
|
||||
await this.setItems();
|
||||
},
|
||||
deactivated() {
|
||||
toggleSidebar(true);
|
||||
},
|
||||
methods: {
|
||||
async setCustomer(value: string) {
|
||||
if (!value) {
|
||||
this.sinvDoc.party = '';
|
||||
return;
|
||||
}
|
||||
|
||||
this.sinvDoc.party = value;
|
||||
|
||||
const party = await this.fyo.db.getAll(ModelNameEnum.Party, {
|
||||
fields: ['loyaltyProgram', 'loyaltyPoints'],
|
||||
filters: { name: value },
|
||||
});
|
||||
|
||||
this.loyaltyProgram = party[0]?.loyaltyProgram as string;
|
||||
this.loyaltyPoints = party[0]?.loyaltyPoints as number;
|
||||
},
|
||||
async saveOrder() {
|
||||
try {
|
||||
await this.validate();
|
||||
await this.sinvDoc.runFormulas();
|
||||
await this.sinvDoc.sync();
|
||||
} catch (error) {
|
||||
return showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: t`Sales Invoice ${this.sinvDoc.name as string} is Saved`,
|
||||
duration: 'short',
|
||||
});
|
||||
|
||||
await this.afterSync();
|
||||
},
|
||||
async setItems() {
|
||||
const items = (await fyo.db.getAll(ModelNameEnum.Item, {
|
||||
fields: [],
|
||||
filters: { trackItem: true },
|
||||
})) as Item[];
|
||||
|
||||
this.items = [] as POSItem[];
|
||||
for (const item of items) {
|
||||
let availableQty = 0;
|
||||
|
||||
if (!!this.itemQtyMap[item.name as string]) {
|
||||
availableQty = this.itemQtyMap[item.name as string].availableQty;
|
||||
}
|
||||
|
||||
if (!item.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.items.push({
|
||||
availableQty,
|
||||
name: item.name,
|
||||
image: item?.image as string,
|
||||
rate: item.rate as Money,
|
||||
unit: item.unit as string,
|
||||
hasBatch: !!item.hasBatch,
|
||||
hasSerialNumber: !!item.hasSerialNumber,
|
||||
});
|
||||
}
|
||||
setTransferRefNo(ref: string) {
|
||||
this.transferRefNo = ref;
|
||||
},
|
||||
toggleView() {
|
||||
this.tableView = !this.tableView;
|
||||
},
|
||||
setCashAmount(amount: Money) {
|
||||
this.cashAmount = amount;
|
||||
},
|
||||
setDefaultCustomer() {
|
||||
this.defaultCustomer = this.fyo.singles.Defaults?.posCustomer ?? '';
|
||||
this.sinvDoc.party = this.defaultCustomer;
|
||||
},
|
||||
setItemDiscounts() {
|
||||
this.itemDiscounts = getItemDiscounts(
|
||||
this.sinvDoc.items as SalesInvoiceItem[]
|
||||
);
|
||||
},
|
||||
async setItemQtyMap() {
|
||||
this.itemQtyMap = await getItemQtyMap();
|
||||
},
|
||||
setSinvDoc() {
|
||||
this.sinvDoc = this.fyo.doc.getNewDoc(ModelNameEnum.SalesInvoice, {
|
||||
account: 'Debtors',
|
||||
party: this.sinvDoc.party ?? this.defaultCustomer,
|
||||
isPOS: true,
|
||||
}) as SalesInvoice;
|
||||
},
|
||||
setCouponCodeDoc() {
|
||||
this.coupons = this.fyo.doc.getNewDoc(
|
||||
ModelNameEnum.AppliedCouponCodes
|
||||
) as AppliedCouponCodes;
|
||||
},
|
||||
setAppliedCoupons() {
|
||||
this.appliedCoupons = this.sinvDoc.coupons as AppliedCouponCodes[];
|
||||
},
|
||||
setTotalQuantity() {
|
||||
this.totalQuantity = getTotalQuantity(
|
||||
this.sinvDoc.items as SalesInvoiceItem[]
|
||||
);
|
||||
},
|
||||
setTotalTaxedAmount() {
|
||||
this.totalTaxedAmount = getTotalTaxedAmount(this.sinvDoc as SalesInvoice);
|
||||
},
|
||||
setCouponsCount(value: number) {
|
||||
this.appliedCouponsCount = value;
|
||||
},
|
||||
async setLoyaltyPoints(value: number) {
|
||||
this.appliedLoyaltyPoints = value;
|
||||
|
||||
this.sinvDoc.redeemLoyaltyPoints = true;
|
||||
|
||||
const totalLotaltyAmount = await getAddedLPWithGrandTotal(
|
||||
this.fyo,
|
||||
this.loyaltyProgram,
|
||||
value
|
||||
);
|
||||
|
||||
const total = totalLotaltyAmount
|
||||
.sub(this.sinvDoc.baseGrandTotal as Money)
|
||||
.abs();
|
||||
|
||||
this.sinvDoc.grandTotal = total;
|
||||
},
|
||||
async selectedInvoiceName(doc: SalesInvoice) {
|
||||
const salesInvoiceDoc = (await this.fyo.doc.getDoc(
|
||||
ModelNameEnum.SalesInvoice,
|
||||
doc.name
|
||||
)) as SalesInvoice;
|
||||
|
||||
this.sinvDoc = salesInvoiceDoc;
|
||||
this.toggleModal('SavedInvoice', false);
|
||||
},
|
||||
setTransferAmount(amount: Money = fyo.pesa(0)) {
|
||||
this.transferAmount = amount;
|
||||
emitSetCashAmount(amount: Money) {
|
||||
this.$emit('setCashAmount', amount);
|
||||
},
|
||||
setTransferClearanceDate(date: Date) {
|
||||
this.transferClearanceDate = date;
|
||||
},
|
||||
setTransferRefNo(ref: string) {
|
||||
this.transferRefNo = ref;
|
||||
emitCouponsCount(value: number) {
|
||||
this.$emit('setCouponsCount', value);
|
||||
},
|
||||
|
||||
async addItem(item: POSItem | Item | undefined) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
await this.sinvDoc.runFormulas();
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.itemQtyMap[item.name as string] ||
|
||||
this.itemQtyMap[item.name as string].availableQty === 0
|
||||
) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
message: t`Item ${item.name as string} has Zero Quantity`,
|
||||
duration: 'short',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const existingItems =
|
||||
this.sinvDoc.items?.filter(
|
||||
(invoiceItem) =>
|
||||
invoiceItem.item === item.name && !invoiceItem.isFreeItem
|
||||
) ?? [];
|
||||
|
||||
if (item.hasBatch) {
|
||||
for (const invItem of existingItems) {
|
||||
const itemQty = invItem.quantity ?? 0;
|
||||
const qtyInBatch =
|
||||
this.itemQtyMap[invItem.item as string][invItem.batch as string] ??
|
||||
0;
|
||||
|
||||
if (itemQty < qtyInBatch) {
|
||||
invItem.quantity = (invItem.quantity as number) + 1;
|
||||
invItem.rate = item.rate as Money;
|
||||
|
||||
await this.applyPricingRule();
|
||||
await this.sinvDoc.runFormulas();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.sinvDoc.append('items', {
|
||||
rate: item.rate as Money,
|
||||
item: item.name,
|
||||
});
|
||||
} catch (error) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingItems.length) {
|
||||
existingItems[0].rate = item.rate as Money;
|
||||
existingItems[0].quantity = (existingItems[0].quantity as number) + 1;
|
||||
await this.applyPricingRule();
|
||||
await this.sinvDoc.runFormulas();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.sinvDoc.append('items', {
|
||||
rate: item.rate as Money,
|
||||
item: item.name,
|
||||
});
|
||||
|
||||
await this.applyPricingRule();
|
||||
await this.sinvDoc.runFormulas();
|
||||
emitSetLoyaltyPoints(value: string) {
|
||||
this.$emit('setLoyaltyPoints', value);
|
||||
},
|
||||
async createTransaction(shouldPrint = false) {
|
||||
try {
|
||||
await this.validate();
|
||||
await this.submitSinvDoc(shouldPrint);
|
||||
await this.makePayment();
|
||||
await this.makeStockTransfer();
|
||||
await this.afterTransaction();
|
||||
await this.setItems();
|
||||
} catch (error) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
emitSelectedInvoice(doc: InvoiceItem) {
|
||||
this.$emit('selectedInvoiceName', doc);
|
||||
},
|
||||
async makePayment() {
|
||||
this.paymentDoc = this.sinvDoc.getPayment() as Payment;
|
||||
const paymentMethod = this.cashAmount.isZero() ? 'Transfer' : 'Cash';
|
||||
await this.paymentDoc.set('paymentMethod', paymentMethod);
|
||||
|
||||
if (paymentMethod === 'Transfer') {
|
||||
await this.paymentDoc.setMultiple({
|
||||
amount: this.transferAmount as Money,
|
||||
referenceId: this.transferRefNo,
|
||||
clearanceDate: this.transferClearanceDate,
|
||||
});
|
||||
}
|
||||
|
||||
if (paymentMethod === 'Cash') {
|
||||
await this.paymentDoc.setMultiple({
|
||||
paymentAccount: this.defaultPOSCashAccount,
|
||||
amount: this.cashAmount as Money,
|
||||
});
|
||||
}
|
||||
|
||||
this.paymentDoc.once('afterSubmit', () => {
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: t`Payment ${this.paymentDoc.name as string} is Saved`,
|
||||
duration: 'short',
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await this.paymentDoc?.sync();
|
||||
await this.paymentDoc?.submit();
|
||||
} catch (error) {
|
||||
return showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
toggleModal(modal: ModalName, value: boolean) {
|
||||
this.$emit('toggleModal', modal, value);
|
||||
},
|
||||
async makeStockTransfer() {
|
||||
const shipmentDoc = (await this.sinvDoc.getStockTransfer()) as Shipment;
|
||||
if (!shipmentDoc.items) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const item of shipmentDoc.items) {
|
||||
item.location = fyo.singles.POSSettings?.inventory;
|
||||
item.serialNumber =
|
||||
this.itemSerialNumbers[item.item as string] ?? undefined;
|
||||
}
|
||||
|
||||
shipmentDoc.once('afterSubmit', () => {
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: t`Shipment ${shipmentDoc.name as string} is Submitted`,
|
||||
duration: 'short',
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await shipmentDoc.sync();
|
||||
await shipmentDoc.submit();
|
||||
} catch (error) {
|
||||
return showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
emitCreateTransaction(shouldPrint = false) {
|
||||
this.$emit('createTransaction', shouldPrint);
|
||||
},
|
||||
emitSetTransferAmount(amount: Money = fyo.pesa(0)) {
|
||||
this.$emit('setTransferAmount', amount);
|
||||
},
|
||||
selectItem(item: POSItem | Item | undefined) {
|
||||
this.$emit('addItem', item);
|
||||
},
|
||||
openCouponModal() {
|
||||
if (this.sinvDoc.party && this.sinvDoc.items?.length) {
|
||||
if (this.sinvDoc?.party && this.sinvDoc?.items?.length) {
|
||||
this.toggleModal('CouponCode', true);
|
||||
}
|
||||
},
|
||||
async submitSinvDoc(shouldPrint: boolean) {
|
||||
this.sinvDoc.once('afterSubmit', async () => {
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: t`Sales Invoice ${this.sinvDoc.name as string} is Submitted`,
|
||||
duration: 'short',
|
||||
});
|
||||
|
||||
if (shouldPrint) {
|
||||
await routeTo(
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
`/print/${this.sinvDoc.schemaName}/${this.sinvDoc.name}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await this.validate();
|
||||
await this.sinvDoc.runFormulas();
|
||||
await this.sinvDoc.sync();
|
||||
await this.sinvDoc.submit();
|
||||
} catch (error) {
|
||||
return showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
},
|
||||
async afterSync() {
|
||||
await this.clearValues();
|
||||
this.setSinvDoc();
|
||||
},
|
||||
async afterTransaction() {
|
||||
await this.setItemQtyMap();
|
||||
await this.clearValues();
|
||||
this.setSinvDoc();
|
||||
this.toggleModal('Payment', false);
|
||||
},
|
||||
async clearValues() {
|
||||
this.setSinvDoc();
|
||||
this.itemSerialNumbers = {};
|
||||
|
||||
this.cashAmount = fyo.pesa(0);
|
||||
this.transferAmount = fyo.pesa(0);
|
||||
await this.setItems();
|
||||
|
||||
if (!this.defaultCustomer) {
|
||||
this.sinvDoc.party = '';
|
||||
}
|
||||
},
|
||||
toggleModal(modal: ModalName, value?: boolean) {
|
||||
if (value) {
|
||||
return (this[`open${modal}Modal`] = value);
|
||||
}
|
||||
return (this[`open${modal}Modal`] = !this[`open${modal}Modal`]);
|
||||
},
|
||||
updateValues() {
|
||||
this.setTotalQuantity();
|
||||
this.setItemDiscounts();
|
||||
this.setTotalTaxedAmount();
|
||||
},
|
||||
async validate() {
|
||||
validateSinv(this.sinvDoc as SalesInvoice, this.itemQtyMap);
|
||||
await validateShipment(this.itemSerialNumbers);
|
||||
},
|
||||
async applyPricingRule() {
|
||||
const hasPricingRules = await getPricingRule(
|
||||
this.sinvDoc as SalesInvoice
|
||||
);
|
||||
|
||||
if (!hasPricingRules || !hasPricingRules.length) {
|
||||
this.sinvDoc.pricingRuleDetail = undefined;
|
||||
this.sinvDoc.isPricingRuleApplied = false;
|
||||
removeFreeItems(this.sinvDoc as SalesInvoice);
|
||||
return;
|
||||
}
|
||||
|
||||
const appliedPricingRuleCount = this.sinvDoc?.items?.filter(
|
||||
(val) => val.isFreeItem
|
||||
).length;
|
||||
|
||||
setTimeout(async () => {
|
||||
if (appliedPricingRuleCount !== hasPricingRules?.length) {
|
||||
await this.sinvDoc.appendPricingRuleDetail(hasPricingRules);
|
||||
await this.sinvDoc.applyProductDiscount();
|
||||
}
|
||||
}, 1);
|
||||
},
|
||||
async routeToSinvList() {
|
||||
if (!this.sinvDoc.items?.length) {
|
||||
return await routeTo('/list/SalesInvoice');
|
||||
}
|
||||
|
||||
this.openRouteToInvoiceListModal = true;
|
||||
},
|
||||
async handleSaveInvoiceAction() {
|
||||
if (!this.sinvDoc.party && !this.sinvDoc.items?.length) {
|
||||
return;
|
||||
}
|
||||
await this.saveOrder();
|
||||
},
|
||||
routeTo,
|
||||
getItem,
|
||||
},
|
||||
|
@ -15,37 +15,35 @@
|
||||
:open-modal="openLoyaltyProgramModal"
|
||||
:loyalty-points="loyaltyPoints"
|
||||
:loyalty-program="loyaltyProgram"
|
||||
@set-loyalty-points="setLoyaltyPoints"
|
||||
@set-loyalty-points="emitSetLoyaltyPoints"
|
||||
@toggle-modal="toggleModal"
|
||||
/>
|
||||
|
||||
<SavedInvoiceModal
|
||||
:open-modal="openSavedInvoiceModal"
|
||||
:modal-status="openSavedInvoiceModal"
|
||||
@selected-invoice-name="selectedInvoiceName"
|
||||
@toggle-modal="toggleModal"
|
||||
@selected-invoice-name="emitSelectedInvoice"
|
||||
/>
|
||||
|
||||
<CouponCodeModal
|
||||
:open-modal="openCouponCodeModal"
|
||||
@set-coupons-count="setCouponsCount"
|
||||
@toggle-modal="toggleModal"
|
||||
@set-coupons-count="emitCouponsCount"
|
||||
/>
|
||||
|
||||
<PaymentModal
|
||||
:open-modal="openPaymentModal"
|
||||
@create-transaction="createTransaction"
|
||||
@toggle-modal="toggleModal"
|
||||
@set-cash-amount="setCashAmount"
|
||||
@set-transfer-amount="setTransferAmount"
|
||||
@set-cash-amount="emitSetCashAmount"
|
||||
@set-coupons-count="emitCouponsCount"
|
||||
@set-transfer-ref-no="setTransferRefNo"
|
||||
@set-coupons-count="setCouponsCount"
|
||||
@create-transaction="emitCreateTransaction"
|
||||
@set-transfer-amount="emitSetTransferAmount"
|
||||
@set-transfer-clearance-date="setTransferClearanceDate"
|
||||
/>
|
||||
|
||||
<AlertModal
|
||||
:open-modal="openRouteToInvoiceListModal"
|
||||
@toggle-modal="toggleModal"
|
||||
/>
|
||||
<AlertModal :open-modal="openAlertModal" @toggle-modal="toggleModal" />
|
||||
|
||||
<KeyboardModal
|
||||
:open-modal="openKeyboardModal"
|
||||
@ -71,13 +69,13 @@
|
||||
>
|
||||
<!-- Customer Search -->
|
||||
<MultiLabelLink
|
||||
v-if="sinvDoc.fieldMap"
|
||||
v-if="sinvDoc?.fieldMap"
|
||||
class="flex-shrink-0"
|
||||
secondary-link="phone"
|
||||
:border="true"
|
||||
:value="sinvDoc.party"
|
||||
:df="sinvDoc.fieldMap.party"
|
||||
@change="(value:string) => setCustomer(value)"
|
||||
:value="sinvDoc?.party"
|
||||
:df="sinvDoc?.fieldMap.party"
|
||||
@change="(value:string) => $emit('setCustomer',value)"
|
||||
/>
|
||||
<ModernPOSSelectedItemTable @toggle-modal="toggleModal" />
|
||||
</div>
|
||||
@ -118,7 +116,7 @@
|
||||
:value="additionalDiscounts"
|
||||
:read-only="true"
|
||||
:text-right="true"
|
||||
@change="(amount:Money)=> additionalDiscounts= amount"
|
||||
@change="(amount:Money)=> additionalDiscounts = amount"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -135,10 +133,10 @@
|
||||
:text-right="true"
|
||||
/>
|
||||
<FloatingLabelCurrencyInput
|
||||
v-if="sinvDoc.fieldMap"
|
||||
:df="sinvDoc.fieldMap.grandTotal"
|
||||
v-if="sinvDoc?.fieldMap"
|
||||
:df="sinvDoc?.fieldMap.grandTotal"
|
||||
size="large"
|
||||
:value="sinvDoc.grandTotal"
|
||||
:value="sinvDoc?.grandTotal"
|
||||
:read-only="true"
|
||||
:text-right="true"
|
||||
/>
|
||||
@ -148,8 +146,8 @@
|
||||
<div class="w-full">
|
||||
<Button
|
||||
class="mt-2 w-full bg-violet-500 dark:bg-violet-700 py-5"
|
||||
:disabled="!sinvDoc.party || !sinvDoc.items?.length"
|
||||
@click="handleSaveInvoiceAction"
|
||||
:disabled="!sinvDoc?.party || !sinvDoc?.items?.length"
|
||||
@click="$emit('saveInvoiceAction')"
|
||||
>
|
||||
<slot>
|
||||
<p class="uppercase text-lg text-white font-semibold">
|
||||
@ -171,8 +169,8 @@
|
||||
<div class="w-full">
|
||||
<Button
|
||||
class="mt-2 w-full bg-red-500 dark:bg-red-700 py-5"
|
||||
:disabled="!sinvDoc.items?.length"
|
||||
@click="clearValues"
|
||||
:disabled="!sinvDoc?.items?.length"
|
||||
@click="() => $emit('clearValues')"
|
||||
>
|
||||
<slot>
|
||||
<p class="uppercase text-lg text-white font-semibold">
|
||||
@ -228,7 +226,7 @@
|
||||
:border="true"
|
||||
:value="itemSearchTerm"
|
||||
@keyup.enter="
|
||||
async () => await addItem(await getItem(itemSearchTerm))
|
||||
async () => await selectItem(await getItem(itemSearchTerm))
|
||||
"
|
||||
@change="(item: string) =>itemSearchTerm= item"
|
||||
/>
|
||||
@ -238,7 +236,7 @@
|
||||
class="w-1/3"
|
||||
@item-selected="
|
||||
async (name: string) => {
|
||||
await addItem(await getItem(name));
|
||||
await selectItem(await getItem(name));
|
||||
}
|
||||
"
|
||||
/>
|
||||
@ -246,14 +244,14 @@
|
||||
<ModernPOSItemsTable
|
||||
v-if="tableView"
|
||||
:items="items"
|
||||
:item-qty-map="itemQtyMap"
|
||||
@add-item="addItem"
|
||||
:item-qty-map="itemQuantityMap as ItemQtyMap"
|
||||
@add-item="selectItem"
|
||||
/>
|
||||
<ModernPOSItemsGrid
|
||||
v-else
|
||||
:items="items"
|
||||
:item-qty-map="itemQtyMap"
|
||||
@add-item="addItem"
|
||||
:item-qty-map="itemQuantityMap as ItemQtyMap"
|
||||
@add-item="selectItem"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -293,7 +291,7 @@
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="px-1.5 py-1 rounded-md bg-gray-100"
|
||||
@click="routeToSinvList"
|
||||
@click="() => $emit('routeToSinvList')"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@ -339,10 +337,10 @@
|
||||
hidden: !fyo.singles.AccountingSettings?.enableLoyaltyProgram,
|
||||
'bg-gray-100': loyaltyPoints,
|
||||
'dark:bg-gray-600 cursor-not-allowed':
|
||||
!loyaltyPoints || !sinvDoc.party || !sinvDoc.items?.length,
|
||||
!loyaltyPoints || !sinvDoc?.party || !sinvDoc?.items?.length,
|
||||
}"
|
||||
@click="
|
||||
loyaltyPoints && sinvDoc.party && sinvDoc.items?.length
|
||||
loyaltyPoints && sinvDoc?.party && sinvDoc?.items?.length
|
||||
? toggleModal('LoyaltyProgram', true)
|
||||
: null
|
||||
"
|
||||
@ -392,8 +390,9 @@
|
||||
hidden: !fyo.singles.AccountingSettings?.enableCouponCode,
|
||||
'bg-gray-100': loyaltyPoints,
|
||||
'dark:bg-gray-600 cursor-not-allowed':
|
||||
!sinvDoc.party || !sinvDoc.items?.length,
|
||||
!sinvDoc?.party || !sinvDoc?.items?.length,
|
||||
}"
|
||||
:disabled="!sinvDoc?.party || !sinvDoc?.items?.length"
|
||||
@click="openCouponModal()"
|
||||
>
|
||||
<svg
|
||||
@ -507,603 +506,172 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Button from 'src/components/Button.vue';
|
||||
import ClosePOSShiftModal from './ClosePOSShiftModal.vue';
|
||||
import FloatingLabelCurrencyInput from 'src/components/POS/FloatingLabelCurrencyInput.vue';
|
||||
import FloatingLabelFloatInput from 'src/components/POS/FloatingLabelFloatInput.vue';
|
||||
import Link from 'src/components/Controls/Link.vue';
|
||||
import OpenPOSShiftModal from './OpenPOSShiftModal.vue';
|
||||
import PaymentModal from './PaymentModal.vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { Money } from 'pesa';
|
||||
import { PropType } from 'vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { routeTo, toggleSidebar } from 'src/utils/ui';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { defineComponent } from 'vue';
|
||||
import { routeTo } from 'src/utils/ui';
|
||||
import { getItem } from 'src/utils/pos';
|
||||
import AlertModal from './AlertModal.vue';
|
||||
import PaymentModal from './PaymentModal.vue';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import KeyboardModal from './KeyboardModal.vue';
|
||||
import { Item } from 'models/baseModels/Item/Item';
|
||||
import Link from 'src/components/Controls/Link.vue';
|
||||
import CouponCodeModal from './CouponCodeModal.vue';
|
||||
import OpenPOSShiftModal from './OpenPOSShiftModal.vue';
|
||||
import SavedInvoiceModal from './SavedInvoiceModal.vue';
|
||||
import Barcode from 'src/components/Controls/Barcode.vue';
|
||||
import ClosePOSShiftModal from './ClosePOSShiftModal.vue';
|
||||
import { Payment } from 'models/baseModels/Payment/Payment';
|
||||
import LoyaltyProgramModal from './LoyaltyprogramModal.vue';
|
||||
import { InvoiceItem } from 'models/baseModels/InvoiceItem/InvoiceItem';
|
||||
import MultiLabelLink from 'src/components/Controls/MultiLabelLink.vue';
|
||||
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
|
||||
import { t } from 'fyo';
|
||||
import ModernPOSItemsGrid from 'src/components/POS/Modern/ModernPOSItemsGrid.vue';
|
||||
import ModernPOSItemsTable from 'src/components/POS/Modern/ModernPOSItemsTable.vue';
|
||||
import FloatingLabelFloatInput from 'src/components/POS/FloatingLabelFloatInput.vue';
|
||||
import FloatingLabelCurrencyInput from 'src/components/POS/FloatingLabelCurrencyInput.vue';
|
||||
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
||||
import ModernPOSSelectedItemTable from 'src/components/POS/Modern/ModernPOSSelectedItemTable.vue';
|
||||
import {
|
||||
ItemQtyMap,
|
||||
ItemSerialNumbers,
|
||||
ModernPosModalName,
|
||||
ModalName,
|
||||
POSItem,
|
||||
} from 'src/components/POS/types';
|
||||
import { Item } from 'models/baseModels/Item/Item';
|
||||
import { Money } from 'pesa';
|
||||
import { Payment } from 'models/baseModels/Payment/Payment';
|
||||
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
|
||||
import { Shipment } from 'models/inventory/Shipment';
|
||||
import { showToast } from 'src/utils/interactive';
|
||||
import {
|
||||
getItem,
|
||||
getItemDiscounts,
|
||||
getItemQtyMap,
|
||||
getTotalQuantity,
|
||||
getTotalTaxedAmount,
|
||||
validateIsPosSettingsSet,
|
||||
validateShipment,
|
||||
validateSinv,
|
||||
} from 'src/utils/pos';
|
||||
import Barcode from 'src/components/Controls/Barcode.vue';
|
||||
import {
|
||||
getAddedLPWithGrandTotal,
|
||||
getPricingRule,
|
||||
removeFreeItems,
|
||||
} from 'models/helpers';
|
||||
import LoyaltyProgramModal from './LoyaltyprogramModal.vue';
|
||||
import AlertModal from './AlertModal.vue';
|
||||
import SavedInvoiceModal from './SavedInvoiceModal.vue';
|
||||
import CouponCodeModal from './CouponCodeModal.vue';
|
||||
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
||||
import ModernPOSSelectedItemTable from 'src/components/POS/Modern/ModernPOSSelectedItemTable.vue';
|
||||
import ModernPOSItemsTable from 'src/components/POS/Modern/ModernPOSItemsTable.vue';
|
||||
import ModernPOSItemsGrid from 'src/components/POS/Modern/ModernPOSItemsGrid.vue';
|
||||
import KeyboardModal from './KeyboardModal.vue';
|
||||
import MultiLabelLink from 'src/components/Controls/MultiLabelLink.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ModernPos',
|
||||
components: {
|
||||
Button,
|
||||
ClosePOSShiftModal,
|
||||
FloatingLabelCurrencyInput,
|
||||
FloatingLabelFloatInput,
|
||||
ModernPOSItemsTable,
|
||||
ModernPOSItemsGrid,
|
||||
Link,
|
||||
AlertModal,
|
||||
OpenPOSShiftModal,
|
||||
PaymentModal,
|
||||
LoyaltyProgramModal,
|
||||
SavedInvoiceModal,
|
||||
CouponCodeModal,
|
||||
ModernPOSSelectedItemTable,
|
||||
Button,
|
||||
Barcode,
|
||||
AlertModal,
|
||||
PaymentModal,
|
||||
KeyboardModal,
|
||||
MultiLabelLink,
|
||||
CouponCodeModal,
|
||||
OpenPOSShiftModal,
|
||||
SavedInvoiceModal,
|
||||
ModernPOSItemsGrid,
|
||||
ClosePOSShiftModal,
|
||||
LoyaltyProgramModal,
|
||||
ModernPOSItemsTable,
|
||||
FloatingLabelFloatInput,
|
||||
FloatingLabelCurrencyInput,
|
||||
ModernPOSSelectedItemTable,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
cashAmount: computed(() => this.cashAmount),
|
||||
doc: computed(() => this.sinvDoc),
|
||||
isDiscountingEnabled: computed(() => this.isDiscountingEnabled),
|
||||
itemDiscounts: computed(() => this.itemDiscounts),
|
||||
itemQtyMap: computed(() => this.itemQtyMap),
|
||||
itemSerialNumbers: computed(() => this.itemSerialNumbers),
|
||||
sinvDoc: computed(() => this.sinvDoc),
|
||||
appliedCoupons: computed(() => this.sinvDoc.coupons),
|
||||
coupons: computed(() => this.coupons),
|
||||
totalTaxedAmount: computed(() => this.totalTaxedAmount),
|
||||
transferAmount: computed(() => this.transferAmount),
|
||||
transferClearanceDate: computed(() => this.transferClearanceDate),
|
||||
transferRefNo: computed(() => this.transferRefNo),
|
||||
};
|
||||
props: {
|
||||
cashAmount: Money,
|
||||
itemDiscounts: Money,
|
||||
openAlertModal: Boolean,
|
||||
disablePayButton: Boolean,
|
||||
openPaymentModal: Boolean,
|
||||
openKeyboardModal: Boolean,
|
||||
coupons: AppliedCouponCodes,
|
||||
openCouponCodeModal: Boolean,
|
||||
openShiftCloseModal: Boolean,
|
||||
openSavedInvoiceModal: Boolean,
|
||||
openLoyaltyProgramModal: Boolean,
|
||||
openAppliedCouponsModal: Boolean,
|
||||
loyaltyPoints: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
loyaltyProgram: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
appliedCouponsCount: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
sinvDoc: {
|
||||
type: SalesInvoice || undefined,
|
||||
default: () => ({}),
|
||||
},
|
||||
itemQuantityMap: {
|
||||
type: Object as PropType<ItemQtyMap>,
|
||||
default: () => ({}),
|
||||
},
|
||||
items: {
|
||||
type: Array as PropType<POSItem[] | undefined>,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
'addItem',
|
||||
'toggleModal',
|
||||
'setCustomer',
|
||||
'clearValues',
|
||||
'setCashAmount',
|
||||
'setCouponsCount',
|
||||
'routeToSinvList',
|
||||
'setLoyaltyPoints',
|
||||
'saveInvoiceAction',
|
||||
'createTransaction',
|
||||
'setTransferAmount',
|
||||
'selectedInvoiceName',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
items: [] as POSItem[],
|
||||
|
||||
tableView: true,
|
||||
|
||||
isItemsSeeded: false,
|
||||
openPaymentModal: false,
|
||||
openLoyaltyProgramModal: false,
|
||||
openSavedInvoiceModal: false,
|
||||
openCouponCodeModal: false,
|
||||
openAppliedCouponsModal: false,
|
||||
openShiftCloseModal: false,
|
||||
openShiftOpenModal: false,
|
||||
openRouteToInvoiceListModal: false,
|
||||
openKeyboardModal: false,
|
||||
|
||||
additionalDiscounts: fyo.pesa(0),
|
||||
cashAmount: fyo.pesa(0),
|
||||
itemDiscounts: fyo.pesa(0),
|
||||
totalTaxedAmount: fyo.pesa(0),
|
||||
transferAmount: fyo.pesa(0),
|
||||
|
||||
totalQuantity: 0,
|
||||
totalTaxedAmount: fyo.pesa(0),
|
||||
additionalDiscounts: fyo.pesa(0),
|
||||
|
||||
loyaltyPoints: 0,
|
||||
appliedLoyaltyPoints: 0,
|
||||
loyaltyProgram: '' as string,
|
||||
paymentDoc: {} as Payment,
|
||||
itemSerialNumbers: {} as ItemSerialNumbers,
|
||||
|
||||
appliedCoupons: [] as AppliedCouponCodes[],
|
||||
appliedCouponsCount: 0,
|
||||
|
||||
defaultCustomer: undefined as string | undefined,
|
||||
itemSearchTerm: '',
|
||||
transferRefNo: undefined as string | undefined,
|
||||
|
||||
transferClearanceDate: undefined as Date | undefined,
|
||||
|
||||
itemQtyMap: {} as ItemQtyMap,
|
||||
itemSerialNumbers: {} as ItemSerialNumbers,
|
||||
paymentDoc: {} as Payment,
|
||||
sinvDoc: {} as SalesInvoice,
|
||||
coupons: {} as AppliedCouponCodes,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
defaultPOSCashAccount: () =>
|
||||
fyo.singles.POSSettings?.cashAccount ?? undefined,
|
||||
isDiscountingEnabled(): boolean {
|
||||
return !!fyo.singles.AccountingSettings?.enableDiscounting;
|
||||
},
|
||||
isPosShiftOpen: () => !!fyo.singles.POSShift?.isShiftOpen,
|
||||
isPaymentAmountSet(): boolean {
|
||||
if (this.sinvDoc.grandTotal?.isZero()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.cashAmount.isZero() && this.transferAmount.isZero()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
disablePayButton(): boolean {
|
||||
if (!this.sinvDoc.items?.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.sinvDoc.party) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
sinvDoc: {
|
||||
handler() {
|
||||
this.updateValues();
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
await this.setItems();
|
||||
},
|
||||
async activated() {
|
||||
toggleSidebar(false);
|
||||
validateIsPosSettingsSet(fyo);
|
||||
this.setCouponCodeDoc();
|
||||
this.setSinvDoc();
|
||||
this.setDefaultCustomer();
|
||||
await this.setItemQtyMap();
|
||||
await this.setItems();
|
||||
},
|
||||
deactivated() {
|
||||
toggleSidebar(true);
|
||||
},
|
||||
methods: {
|
||||
async setCustomer(value: string) {
|
||||
if (!value) {
|
||||
this.sinvDoc.party = '';
|
||||
return;
|
||||
}
|
||||
|
||||
this.sinvDoc.party = value;
|
||||
|
||||
const party = await this.fyo.db.getAll(ModelNameEnum.Party, {
|
||||
fields: ['loyaltyProgram', 'loyaltyPoints'],
|
||||
filters: { name: value },
|
||||
});
|
||||
|
||||
this.loyaltyProgram = party[0]?.loyaltyProgram as string;
|
||||
this.loyaltyPoints = party[0]?.loyaltyPoints as number;
|
||||
},
|
||||
async saveOrder() {
|
||||
try {
|
||||
await this.validate();
|
||||
await this.sinvDoc.runFormulas();
|
||||
await this.sinvDoc.sync();
|
||||
} catch (error) {
|
||||
return showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: t`Sales Invoice ${this.sinvDoc.name as string} is Saved`,
|
||||
duration: 'short',
|
||||
});
|
||||
|
||||
await this.afterSync();
|
||||
},
|
||||
async setItems() {
|
||||
const items = (await fyo.db.getAll(ModelNameEnum.Item, {
|
||||
fields: [],
|
||||
filters: { trackItem: true },
|
||||
})) as Item[];
|
||||
|
||||
this.items = [] as POSItem[];
|
||||
for (const item of items) {
|
||||
let availableQty = 0;
|
||||
|
||||
if (!!this.itemQtyMap[item.name as string]) {
|
||||
availableQty = this.itemQtyMap[item.name as string].availableQty;
|
||||
}
|
||||
|
||||
if (!item.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.items.push({
|
||||
availableQty,
|
||||
name: item.name,
|
||||
image: item?.image as string,
|
||||
rate: item.rate as Money,
|
||||
unit: item.unit as string,
|
||||
hasBatch: !!item.hasBatch,
|
||||
hasSerialNumber: !!item.hasSerialNumber,
|
||||
});
|
||||
}
|
||||
setTransferRefNo(ref: string) {
|
||||
this.transferRefNo = ref;
|
||||
},
|
||||
toggleView() {
|
||||
this.tableView = !this.tableView;
|
||||
},
|
||||
setCashAmount(amount: Money) {
|
||||
this.cashAmount = amount;
|
||||
},
|
||||
setDefaultCustomer() {
|
||||
this.defaultCustomer = this.fyo.singles.Defaults?.posCustomer ?? '';
|
||||
this.sinvDoc.party = this.defaultCustomer;
|
||||
},
|
||||
setItemDiscounts() {
|
||||
this.itemDiscounts = getItemDiscounts(
|
||||
this.sinvDoc.items as SalesInvoiceItem[]
|
||||
);
|
||||
},
|
||||
async setItemQtyMap() {
|
||||
this.itemQtyMap = await getItemQtyMap();
|
||||
},
|
||||
setSinvDoc() {
|
||||
this.sinvDoc = this.fyo.doc.getNewDoc(ModelNameEnum.SalesInvoice, {
|
||||
account: 'Debtors',
|
||||
party: this.sinvDoc.party ?? this.defaultCustomer,
|
||||
isPOS: true,
|
||||
}) as SalesInvoice;
|
||||
},
|
||||
setCouponCodeDoc() {
|
||||
this.coupons = this.fyo.doc.getNewDoc(
|
||||
ModelNameEnum.AppliedCouponCodes
|
||||
) as AppliedCouponCodes;
|
||||
},
|
||||
setAppliedCoupons() {
|
||||
this.appliedCoupons = this.sinvDoc.coupons as AppliedCouponCodes[];
|
||||
},
|
||||
setTotalQuantity() {
|
||||
this.totalQuantity = getTotalQuantity(
|
||||
this.sinvDoc.items as SalesInvoiceItem[]
|
||||
);
|
||||
},
|
||||
setTotalTaxedAmount() {
|
||||
this.totalTaxedAmount = getTotalTaxedAmount(this.sinvDoc as SalesInvoice);
|
||||
},
|
||||
setCouponsCount(value: number) {
|
||||
this.appliedCouponsCount = value;
|
||||
},
|
||||
async setLoyaltyPoints(value: number) {
|
||||
this.appliedLoyaltyPoints = value;
|
||||
|
||||
this.sinvDoc.redeemLoyaltyPoints = true;
|
||||
|
||||
const totalLotaltyAmount = await getAddedLPWithGrandTotal(
|
||||
this.fyo,
|
||||
this.loyaltyProgram,
|
||||
value
|
||||
);
|
||||
|
||||
const total = totalLotaltyAmount
|
||||
.sub(this.sinvDoc.baseGrandTotal as Money)
|
||||
.abs();
|
||||
|
||||
this.sinvDoc.grandTotal = total;
|
||||
},
|
||||
async selectedInvoiceName(doc: SalesInvoice) {
|
||||
const salesInvoiceDoc = (await this.fyo.doc.getDoc(
|
||||
ModelNameEnum.SalesInvoice,
|
||||
doc.name
|
||||
)) as SalesInvoice;
|
||||
|
||||
this.sinvDoc = salesInvoiceDoc;
|
||||
this.toggleModal('SavedInvoice', false);
|
||||
},
|
||||
setTransferAmount(amount: Money = fyo.pesa(0)) {
|
||||
this.transferAmount = amount;
|
||||
emitSetCashAmount(amount: Money) {
|
||||
this.$emit('setCashAmount', amount);
|
||||
},
|
||||
setTransferClearanceDate(date: Date) {
|
||||
this.transferClearanceDate = date;
|
||||
},
|
||||
setTransferRefNo(ref: string) {
|
||||
this.transferRefNo = ref;
|
||||
emitCouponsCount(value: number) {
|
||||
this.$emit('setCouponsCount', value);
|
||||
},
|
||||
|
||||
async addItem(item: POSItem | Item | undefined) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
await this.sinvDoc.runFormulas();
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.itemQtyMap[item.name as string] ||
|
||||
this.itemQtyMap[item.name as string].availableQty === 0
|
||||
) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
message: t`Item ${item.name as string} has Zero Quantity`,
|
||||
duration: 'short',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const existingItems =
|
||||
this.sinvDoc.items?.filter(
|
||||
(invoiceItem) =>
|
||||
invoiceItem.item === item.name && !invoiceItem.isFreeItem
|
||||
) ?? [];
|
||||
|
||||
if (item.hasBatch) {
|
||||
for (const invItem of existingItems) {
|
||||
const itemQty = invItem.quantity ?? 0;
|
||||
const qtyInBatch =
|
||||
this.itemQtyMap[invItem.item as string][invItem.batch as string] ??
|
||||
0;
|
||||
|
||||
if (itemQty < qtyInBatch) {
|
||||
invItem.quantity = (invItem.quantity as number) + 1;
|
||||
invItem.rate = item.rate as Money;
|
||||
|
||||
await this.applyPricingRule();
|
||||
await this.sinvDoc.runFormulas();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.sinvDoc.append('items', {
|
||||
rate: item.rate as Money,
|
||||
item: item.name,
|
||||
});
|
||||
} catch (error) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingItems.length) {
|
||||
existingItems[0].rate = item.rate as Money;
|
||||
existingItems[0].quantity = (existingItems[0].quantity as number) + 1;
|
||||
await this.applyPricingRule();
|
||||
await this.sinvDoc.runFormulas();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.sinvDoc.append('items', {
|
||||
rate: item.rate as Money,
|
||||
item: item.name,
|
||||
});
|
||||
|
||||
await this.applyPricingRule();
|
||||
await this.sinvDoc.runFormulas();
|
||||
emitSetLoyaltyPoints(value: string) {
|
||||
this.$emit('setLoyaltyPoints', value);
|
||||
},
|
||||
async createTransaction(shouldPrint = false) {
|
||||
try {
|
||||
await this.validate();
|
||||
await this.submitSinvDoc(shouldPrint);
|
||||
await this.makePayment();
|
||||
await this.makeStockTransfer();
|
||||
await this.afterTransaction();
|
||||
await this.setItems();
|
||||
} catch (error) {
|
||||
showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
emitSelectedInvoice(doc: InvoiceItem) {
|
||||
this.$emit('selectedInvoiceName', doc);
|
||||
},
|
||||
async makePayment() {
|
||||
this.paymentDoc = this.sinvDoc.getPayment() as Payment;
|
||||
const paymentMethod = this.cashAmount.isZero() ? 'Transfer' : 'Cash';
|
||||
await this.paymentDoc.set('paymentMethod', paymentMethod);
|
||||
|
||||
if (paymentMethod === 'Transfer') {
|
||||
await this.paymentDoc.setMultiple({
|
||||
amount: this.transferAmount as Money,
|
||||
referenceId: this.transferRefNo,
|
||||
clearanceDate: this.transferClearanceDate,
|
||||
});
|
||||
}
|
||||
|
||||
if (paymentMethod === 'Cash') {
|
||||
await this.paymentDoc.setMultiple({
|
||||
paymentAccount: this.defaultPOSCashAccount,
|
||||
amount: this.cashAmount as Money,
|
||||
});
|
||||
}
|
||||
|
||||
this.paymentDoc.once('afterSubmit', () => {
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: t`Payment ${this.paymentDoc.name as string} is Saved`,
|
||||
duration: 'short',
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await this.paymentDoc?.sync();
|
||||
await this.paymentDoc?.submit();
|
||||
} catch (error) {
|
||||
return showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
toggleModal(modal: ModalName, value: boolean) {
|
||||
this.$emit('toggleModal', modal, value);
|
||||
},
|
||||
async makeStockTransfer() {
|
||||
const shipmentDoc = (await this.sinvDoc.getStockTransfer()) as Shipment;
|
||||
if (!shipmentDoc.items) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const item of shipmentDoc.items) {
|
||||
item.location = fyo.singles.POSSettings?.inventory;
|
||||
item.serialNumber =
|
||||
this.itemSerialNumbers[item.item as string] ?? undefined;
|
||||
}
|
||||
|
||||
shipmentDoc.once('afterSubmit', () => {
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: t`Shipment ${shipmentDoc.name as string} is Submitted`,
|
||||
duration: 'short',
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await shipmentDoc.sync();
|
||||
await shipmentDoc.submit();
|
||||
} catch (error) {
|
||||
return showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
emitCreateTransaction(shouldPrint = false) {
|
||||
this.$emit('createTransaction', shouldPrint);
|
||||
},
|
||||
emitSetTransferAmount(amount: Money = fyo.pesa(0)) {
|
||||
this.$emit('setTransferAmount', amount);
|
||||
},
|
||||
selectItem(item: POSItem | Item | undefined) {
|
||||
this.$emit('addItem', item);
|
||||
},
|
||||
openCouponModal() {
|
||||
if (this.sinvDoc.party && this.sinvDoc.items?.length) {
|
||||
if (this.sinvDoc?.party && this.sinvDoc?.items?.length) {
|
||||
this.toggleModal('CouponCode', true);
|
||||
}
|
||||
},
|
||||
async submitSinvDoc(shouldPrint: boolean) {
|
||||
this.sinvDoc.once('afterSubmit', async () => {
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: t`Sales Invoice ${this.sinvDoc.name as string} is Submitted`,
|
||||
duration: 'short',
|
||||
});
|
||||
|
||||
if (shouldPrint) {
|
||||
await routeTo(
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||
`/print/${this.sinvDoc.schemaName}/${this.sinvDoc.name}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await this.validate();
|
||||
await this.sinvDoc.runFormulas();
|
||||
await this.sinvDoc.sync();
|
||||
await this.sinvDoc.submit();
|
||||
} catch (error) {
|
||||
return showToast({
|
||||
type: 'error',
|
||||
message: t`${error as string}`,
|
||||
});
|
||||
}
|
||||
},
|
||||
async afterSync() {
|
||||
await this.clearValues();
|
||||
this.setSinvDoc();
|
||||
},
|
||||
async afterTransaction() {
|
||||
await this.setItemQtyMap();
|
||||
await this.clearValues();
|
||||
this.setSinvDoc();
|
||||
this.toggleModal('Payment', false);
|
||||
},
|
||||
async clearValues() {
|
||||
this.setSinvDoc();
|
||||
this.itemSerialNumbers = {};
|
||||
|
||||
this.cashAmount = fyo.pesa(0);
|
||||
this.transferAmount = fyo.pesa(0);
|
||||
await this.setItems();
|
||||
|
||||
if (!this.defaultCustomer) {
|
||||
this.sinvDoc.party = '';
|
||||
}
|
||||
},
|
||||
toggleModal(modal: ModernPosModalName, value?: boolean) {
|
||||
if (value) {
|
||||
return (this[`open${modal}Modal`] = value);
|
||||
}
|
||||
return (this[`open${modal}Modal`] = !this[`open${modal}Modal`]);
|
||||
},
|
||||
updateValues() {
|
||||
this.setTotalQuantity();
|
||||
this.setItemDiscounts();
|
||||
this.setTotalTaxedAmount();
|
||||
},
|
||||
async validate() {
|
||||
validateSinv(this.sinvDoc as SalesInvoice, this.itemQtyMap);
|
||||
await validateShipment(this.itemSerialNumbers);
|
||||
},
|
||||
async applyPricingRule() {
|
||||
const hasPricingRules = await getPricingRule(
|
||||
this.sinvDoc as SalesInvoice
|
||||
);
|
||||
|
||||
if (!hasPricingRules || !hasPricingRules.length) {
|
||||
this.sinvDoc.pricingRuleDetail = undefined;
|
||||
this.sinvDoc.isPricingRuleApplied = false;
|
||||
removeFreeItems(this.sinvDoc as SalesInvoice);
|
||||
return;
|
||||
}
|
||||
|
||||
const appliedPricingRuleCount = this.sinvDoc?.items?.filter(
|
||||
(val) => val.isFreeItem
|
||||
).length;
|
||||
|
||||
setTimeout(async () => {
|
||||
if (appliedPricingRuleCount !== hasPricingRules?.length) {
|
||||
await this.sinvDoc.appendPricingRuleDetail(hasPricingRules);
|
||||
await this.sinvDoc.applyProductDiscount();
|
||||
}
|
||||
}, 1);
|
||||
},
|
||||
async routeToSinvList() {
|
||||
if (!this.sinvDoc.items?.length) {
|
||||
return await routeTo('/list/SalesInvoice');
|
||||
}
|
||||
|
||||
this.openRouteToInvoiceListModal = true;
|
||||
},
|
||||
async handleSaveInvoiceAction() {
|
||||
if (!this.sinvDoc.party && !this.sinvDoc.items?.length) {
|
||||
return;
|
||||
}
|
||||
await this.saveOrder();
|
||||
},
|
||||
routeTo,
|
||||
getItem,
|
||||
},
|
||||
|
@ -16,32 +16,95 @@
|
||||
</Button>
|
||||
</slot>
|
||||
</PageHeader>
|
||||
<ClassicPOS v-if="fyo.singles.POSSettings?.posUI == 'Classic'" />
|
||||
<ModernPOS v-else />
|
||||
<ClassicPOS
|
||||
v-if="fyo.singles.POSSettings?.posUI == 'Classic'"
|
||||
:item-quantity-qap="itemQtyMap"
|
||||
:loyalty-points="loyaltyPoints"
|
||||
:open-alert-modal="openAlertModal"
|
||||
:default-customer="defaultCustomer"
|
||||
:items="(items as [] as POSItem[])"
|
||||
:cash-amount="(cashAmount as Money)"
|
||||
:sinv-doc="(sinvDoc as SalesInvoice)"
|
||||
:disable-pay-button="disablePayButton"
|
||||
:open-payment-modal="openPaymentModal"
|
||||
:item-discounts="(itemDiscounts as Money)"
|
||||
:coupons="(coupons as AppliedCouponCodes)"
|
||||
:applied-coupons-count="appliedCouponsCount"
|
||||
:open-shift-close-modal="openShiftCloseModal"
|
||||
:open-coupon-code-modal="openCouponCodeModal"
|
||||
:open-saved-invoice-modal="openSavedInvoiceModal"
|
||||
:open-loyalty-program-modal="openLoyaltyProgramModal"
|
||||
:open-applied-coupons-modal="openAppliedCouponsModal"
|
||||
@add-item="addItem"
|
||||
@set-sinv-doc="setSinvDoc"
|
||||
@clear-values="clearValues"
|
||||
@set-customer="setCustomer"
|
||||
@toggle-modal="toggleModal"
|
||||
@set-cash-amount="setCashAmount"
|
||||
@route-to-sinv-list="routeToSinvList"
|
||||
@set-coupons-count="setCouponsCount"
|
||||
@set-loyalty-points="setLoyaltyPoints"
|
||||
@save-invoice-action="saveInvoiceAction"
|
||||
@create-transaction="createTransaction"
|
||||
@set-transfer-amount="setTransferAmount"
|
||||
@selected-invoice-name="selectedInvoiceName"
|
||||
/>
|
||||
<ModernPOS
|
||||
v-else
|
||||
:item-quantity-qap="itemQtyMap"
|
||||
:loyalty-points="loyaltyPoints"
|
||||
:open-alert-modal="openAlertModal"
|
||||
:default-customer="defaultCustomer"
|
||||
:items="(items as [] as POSItem[])"
|
||||
:cash-amount="(cashAmount as Money)"
|
||||
:sinv-doc="(sinvDoc as SalesInvoice)"
|
||||
:disable-pay-button="disablePayButton"
|
||||
:open-payment-modal="openPaymentModal"
|
||||
:open-keyboard-modal="openKeyboardModal"
|
||||
:item-discounts="(itemDiscounts as Money)"
|
||||
:coupons="(coupons as AppliedCouponCodes)"
|
||||
:applied-coupons-count="appliedCouponsCount"
|
||||
:open-shift-close-modal="openShiftCloseModal"
|
||||
:open-coupon-code-modal="openCouponCodeModal"
|
||||
:open-saved-invoice-modal="openSavedInvoiceModal"
|
||||
:open-loyalty-program-modal="openLoyaltyProgramModal"
|
||||
:open-applied-coupons-modal="openAppliedCouponsModal"
|
||||
@add-item="addItem"
|
||||
@set-sinv-doc="setSinvDoc"
|
||||
@clear-values="clearValues"
|
||||
@set-customer="setCustomer"
|
||||
@toggle-modal="toggleModal"
|
||||
@set-cash-amount="setCashAmount"
|
||||
@route-to-sinv-list="routeToSinvList"
|
||||
@set-coupons-count="setCouponsCount"
|
||||
@set-loyalty-points="setLoyaltyPoints"
|
||||
@save-invoice-action="saveInvoiceAction"
|
||||
@create-transaction="createTransaction"
|
||||
@set-transfer-amount="setTransferAmount"
|
||||
@selected-invoice-name="selectedInvoiceName"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Button from 'src/components/Button.vue';
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { routeTo, toggleSidebar } from 'src/utils/ui';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
|
||||
import { t } from 'fyo';
|
||||
import {
|
||||
ItemQtyMap,
|
||||
ItemSerialNumbers,
|
||||
POSItem,
|
||||
} from 'src/components/POS/types';
|
||||
import { Money } from 'pesa';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import ModernPOS from './ModernPOS.vue';
|
||||
import ClassicPOS from './ClassicPOS.vue';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { showToast } from 'src/utils/interactive';
|
||||
import { Item } from 'models/baseModels/Item/Item';
|
||||
import { ModalName } from 'src/components/POS/types';
|
||||
import { Money } from 'pesa';
|
||||
import { Payment } from 'models/baseModels/Payment/Payment';
|
||||
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
|
||||
import { Shipment } from 'models/inventory/Shipment';
|
||||
import { showToast } from 'src/utils/interactive';
|
||||
import { routeTo, toggleSidebar } from 'src/utils/ui';
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import { Payment } from 'models/baseModels/Payment/Payment';
|
||||
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
|
||||
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
|
||||
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
||||
import {
|
||||
getItem,
|
||||
getItemDiscounts,
|
||||
@ -57,77 +120,76 @@ import {
|
||||
getPricingRule,
|
||||
removeFreeItems,
|
||||
} from 'models/helpers';
|
||||
import { AppliedCouponCodes } from 'models/baseModels/AppliedCouponCodes/AppliedCouponCodes';
|
||||
import ClassicPOS from './ClassicPOS.vue';
|
||||
import ModernPOS from './ModernPOS.vue';
|
||||
import {
|
||||
ItemQtyMap,
|
||||
ItemSerialNumbers,
|
||||
POSItem,
|
||||
} from 'src/components/POS/types';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'POS',
|
||||
components: {
|
||||
Button,
|
||||
ModernPOS,
|
||||
PageHeader,
|
||||
ClassicPOS,
|
||||
ModernPOS,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
cashAmount: computed(() => this.cashAmount),
|
||||
doc: computed(() => this.sinvDoc),
|
||||
isDiscountingEnabled: computed(() => this.isDiscountingEnabled),
|
||||
itemDiscounts: computed(() => this.itemDiscounts),
|
||||
itemQtyMap: computed(() => this.itemQtyMap),
|
||||
itemSerialNumbers: computed(() => this.itemSerialNumbers),
|
||||
sinvDoc: computed(() => this.sinvDoc),
|
||||
appliedCoupons: computed(() => this.sinvDoc.coupons),
|
||||
coupons: computed(() => this.coupons),
|
||||
totalTaxedAmount: computed(() => this.totalTaxedAmount),
|
||||
transferAmount: computed(() => this.transferAmount),
|
||||
transferClearanceDate: computed(() => this.transferClearanceDate),
|
||||
itemQtyMap: computed(() => this.itemQtyMap),
|
||||
cashAmount: computed(() => this.cashAmount),
|
||||
transferRefNo: computed(() => this.transferRefNo),
|
||||
itemDiscounts: computed(() => this.itemDiscounts),
|
||||
transferAmount: computed(() => this.transferAmount),
|
||||
appliedCoupons: computed(() => this.sinvDoc.coupons),
|
||||
totalTaxedAmount: computed(() => this.totalTaxedAmount),
|
||||
itemSerialNumbers: computed(() => this.itemSerialNumbers),
|
||||
isDiscountingEnabled: computed(() => this.isDiscountingEnabled),
|
||||
transferClearanceDate: computed(() => this.transferClearanceDate),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [] as POSItem[],
|
||||
|
||||
tableView: true,
|
||||
|
||||
isItemsSeeded: false,
|
||||
openPaymentModal: false,
|
||||
openLoyaltyProgramModal: false,
|
||||
openSavedInvoiceModal: false,
|
||||
openCouponCodeModal: false,
|
||||
openAppliedCouponsModal: false,
|
||||
openShiftCloseModal: false,
|
||||
openShiftOpenModal: false,
|
||||
openRouteToInvoiceListModal: false,
|
||||
items: [] as POSItem[],
|
||||
|
||||
additionalDiscounts: fyo.pesa(0),
|
||||
cashAmount: fyo.pesa(0),
|
||||
itemDiscounts: fyo.pesa(0),
|
||||
totalTaxedAmount: fyo.pesa(0),
|
||||
transferAmount: fyo.pesa(0),
|
||||
openAlertModal: false,
|
||||
openPaymentModal: false,
|
||||
openKeyboardModal: false,
|
||||
openCouponCodeModal: false,
|
||||
openShiftCloseModal: false,
|
||||
openSavedInvoiceModal: false,
|
||||
openLoyaltyProgramModal: false,
|
||||
openAppliedCouponsModal: false,
|
||||
|
||||
totalQuantity: 0,
|
||||
cashAmount: fyo.pesa(0),
|
||||
itemDiscounts: fyo.pesa(0),
|
||||
transferAmount: fyo.pesa(0),
|
||||
totalTaxedAmount: fyo.pesa(0),
|
||||
additionalDiscounts: fyo.pesa(0),
|
||||
|
||||
loyaltyPoints: 0,
|
||||
appliedLoyaltyPoints: 0,
|
||||
loyaltyProgram: '' as string,
|
||||
|
||||
appliedCoupons: [] as AppliedCouponCodes[],
|
||||
appliedCouponsCount: 0,
|
||||
appliedCoupons: [] as AppliedCouponCodes[],
|
||||
|
||||
defaultCustomer: undefined as string | undefined,
|
||||
itemSearchTerm: '',
|
||||
transferRefNo: undefined as string | undefined,
|
||||
|
||||
defaultCustomer: undefined as string | undefined,
|
||||
transferClearanceDate: undefined as Date | undefined,
|
||||
|
||||
itemQtyMap: {} as ItemQtyMap,
|
||||
itemSerialNumbers: {} as ItemSerialNumbers,
|
||||
paymentDoc: {} as Payment,
|
||||
sinvDoc: {} as SalesInvoice,
|
||||
itemQtyMap: {} as ItemQtyMap,
|
||||
coupons: {} as AppliedCouponCodes,
|
||||
itemSerialNumbers: {} as ItemSerialNumbers,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -137,24 +199,11 @@ export default defineComponent({
|
||||
return !!fyo.singles.AccountingSettings?.enableDiscounting;
|
||||
},
|
||||
isPosShiftOpen: () => !!fyo.singles.POSShift?.isShiftOpen,
|
||||
isPaymentAmountSet(): boolean {
|
||||
if (this.sinvDoc.grandTotal?.isZero()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.cashAmount.isZero() && this.transferAmount.isZero()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
disablePayButton(): boolean {
|
||||
if (!this.sinvDoc.items?.length) {
|
||||
if (!this.sinvDoc.items?.length || !this.sinvDoc.party) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.sinvDoc.party) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
@ -186,6 +235,7 @@ export default defineComponent({
|
||||
async setCustomer(value: string) {
|
||||
if (!value) {
|
||||
this.sinvDoc.party = '';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -390,8 +440,10 @@ export default defineComponent({
|
||||
if (existingItems.length) {
|
||||
existingItems[0].rate = item.rate as Money;
|
||||
existingItems[0].quantity = (existingItems[0].quantity as number) + 1;
|
||||
|
||||
await this.applyPricingRule();
|
||||
await this.sinvDoc.runFormulas();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -421,6 +473,7 @@ export default defineComponent({
|
||||
async makePayment() {
|
||||
this.paymentDoc = this.sinvDoc.getPayment() as Payment;
|
||||
const paymentMethod = this.cashAmount.isZero() ? 'Transfer' : 'Cash';
|
||||
|
||||
await this.paymentDoc.set('paymentMethod', paymentMethod);
|
||||
|
||||
if (paymentMethod === 'Transfer') {
|
||||
@ -545,6 +598,7 @@ export default defineComponent({
|
||||
if (value) {
|
||||
return (this[`open${modal}Modal`] = value);
|
||||
}
|
||||
|
||||
return (this[`open${modal}Modal`] = !this[`open${modal}Modal`]);
|
||||
},
|
||||
updateValues() {
|
||||
@ -565,6 +619,7 @@ export default defineComponent({
|
||||
this.sinvDoc.pricingRuleDetail = undefined;
|
||||
this.sinvDoc.isPricingRuleApplied = false;
|
||||
removeFreeItems(this.sinvDoc as SalesInvoice);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -591,12 +646,13 @@ export default defineComponent({
|
||||
return await routeTo('/list/SalesInvoice');
|
||||
}
|
||||
|
||||
this.openRouteToInvoiceListModal = true;
|
||||
this.openAlertModal = true;
|
||||
},
|
||||
async handleSaveInvoiceAction() {
|
||||
async saveInvoiceAction() {
|
||||
if (!this.sinvDoc.party && !this.sinvDoc.items?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.saveOrder();
|
||||
},
|
||||
routeTo,
|
||||
|
Loading…
Reference in New Issue
Block a user