2
0
mirror of https://github.com/frappe/books.git synced 2025-01-03 07:12:21 +00:00

Merge pull request #975 from AbleKSaju/feat-pos-save-order

feat: save invoice and hold invoice for later use in POS
This commit is contained in:
Akshay 2024-10-15 11:38:27 +05:30 committed by GitHub
commit 6fb81a56d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 284 additions and 27 deletions

View File

@ -8,7 +8,7 @@ export type ItemSerialNumbers = { [item: string]: string };
export type DiscountType = "percent" | "amount"; export type DiscountType = "percent" | "amount";
export type ModalName = 'ShiftOpen' | 'ShiftClose' | 'Payment' | 'LoyaltyProgram' | 'RouteToInvoiceList' export type ModalName = 'ShiftOpen' | 'ShiftClose' | 'Payment' | 'LoyaltyProgram' | 'SavedInvoice' | 'RouteToInvoiceList'
export interface POSItem { export interface POSItem {
image?:string, image?:string,

View File

@ -29,6 +29,12 @@
@set-loyalty-points="setLoyaltyPoints" @set-loyalty-points="setLoyaltyPoints"
@toggle-modal="toggleModal" @toggle-modal="toggleModal"
/> />
<SavedInvoiceModal
:open-modal="openSavedInvoiceModal"
:modal-status="openSavedInvoiceModal"
@selected-invoice-name="selectedInvoiceName"
@toggle-modal="toggleModal"
/>
<PaymentModal <PaymentModal
:open-modal="openPaymentModal" :open-modal="openPaymentModal"
@ -325,31 +331,55 @@
/> />
</div> </div>
</div> </div>
<div class="flex w-full gap-2">
<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"
>
<slot>
<p class="uppercase text-lg text-white font-semibold">
{{ t`Save` }}
</p>
</slot>
</Button>
<Button
class="w-full mt-4 bg-blue-500 dark:bg-blue-700 py-6"
@click="toggleModal('SavedInvoice', true)"
>
<slot>
<p class="uppercase text-lg text-white font-semibold">
{{ t`held` }}
</p>
</slot>
</Button>
</div>
<div class="w-full">
<Button
class="w-full bg-red-500 dark:bg-red-700 py-6"
:disabled="!sinvDoc.items?.length"
@click="clearValues"
>
<slot>
<p class="uppercase text-lg text-white font-semibold">
{{ t`Cancel` }}
</p>
</slot>
</Button>
<div class=""> <Button
<Button class="mt-4 w-full bg-green-500 dark:bg-green-700 py-6"
class="w-full bg-red-500 dark:bg-red-700 py-6" :disabled="disablePayButton"
:disabled="!sinvDoc.items?.length" @click="toggleModal('Payment', true)"
@click="clearValues" >
> <slot>
<slot> <p class="uppercase text-lg text-white font-semibold">
<p class="uppercase text-lg text-white font-semibold"> {{ t`Pay` }}
{{ t`Cancel` }} </p>
</p> </slot>
</slot> </Button>
</Button> </div>
<Button
class="mt-4 w-full bg-green-500 dark:bg-green-700 py-6"
:disabled="disablePayButton"
@click="toggleModal('Payment', true)"
>
<slot>
<p class="uppercase text-lg text-white font-semibold">
{{ t`Pay` }}
</p>
</slot>
</Button>
</div> </div>
</div> </div>
</div> </div>
@ -403,6 +433,7 @@ import Barcode from 'src/components/Controls/Barcode.vue';
import { getAddedLPWithGrandTotal, getPricingRule } from 'models/helpers'; import { getAddedLPWithGrandTotal, getPricingRule } from 'models/helpers';
import LoyaltyProgramModal from './LoyaltyprogramModal.vue'; import LoyaltyProgramModal from './LoyaltyprogramModal.vue';
import AlertModal from './AlertModal.vue'; import AlertModal from './AlertModal.vue';
import SavedInvoiceModal from './SavedInvoiceModal.vue';
export default defineComponent({ export default defineComponent({
name: 'POS', name: 'POS',
@ -419,6 +450,7 @@ export default defineComponent({
PageHeader, PageHeader,
PaymentModal, PaymentModal,
LoyaltyProgramModal, LoyaltyProgramModal,
SavedInvoiceModal,
SelectedItemTable, SelectedItemTable,
Barcode, Barcode,
}, },
@ -446,6 +478,7 @@ export default defineComponent({
isItemsSeeded: false, isItemsSeeded: false,
openPaymentModal: false, openPaymentModal: false,
openLoyaltyProgramModal: false, openLoyaltyProgramModal: false,
openSavedInvoiceModal: false,
openShiftCloseModal: false, openShiftCloseModal: false,
openShiftOpenModal: false, openShiftOpenModal: false,
openRouteToInvoiceListModal: false, openRouteToInvoiceListModal: false,
@ -541,6 +574,26 @@ export default defineComponent({
this.loyaltyProgram = party[0]?.loyaltyProgram as string; this.loyaltyProgram = party[0]?.loyaltyProgram as string;
this.loyaltyPoints = party[0].loyaltyPoints as number; 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() { async setItems() {
const items = (await fyo.db.getAll(ModelNameEnum.Item, { const items = (await fyo.db.getAll(ModelNameEnum.Item, {
fields: [], fields: [],
@ -620,6 +673,15 @@ export default defineComponent({
this.sinvDoc.grandTotal = total; 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)) { setTransferAmount(amount: Money = fyo.pesa(0)) {
this.transferAmount = amount; this.transferAmount = amount;
}, },
@ -832,7 +894,10 @@ export default defineComponent({
}); });
} }
}, },
async afterSync() {
await this.clearValues();
this.setSinvDoc();
},
async afterTransaction() { async afterTransaction() {
await this.setItemQtyMap(); await this.setItemQtyMap();
await this.clearValues(); await this.clearValues();
@ -890,12 +955,18 @@ export default defineComponent({
}, 1); }, 1);
}, },
async routeToSinvList() { async routeToSinvList() {
if (!this.sinvDoc.items.length) { if (!this.sinvDoc.items?.length) {
return await routeTo('/list/SalesInvoice'); return await routeTo('/list/SalesInvoice');
} }
this.openRouteToInvoiceListModal = true; this.openRouteToInvoiceListModal = true;
}, },
async handleSaveInvoiceAction() {
if (!this.sinvDoc.party && !this.sinvDoc.items?.length) {
return;
}
await this.saveOrder();
},
routeTo, routeTo,
getItem, getItem,
}, },

View File

@ -0,0 +1,186 @@
<template>
<Modal class="h-auto w-auto p-6" :set-close-listener="false">
<p class="text-center font-semibold">{{ t`Saved Invoices` }}</p>
<hr class="mt-2 dark:border-gray-800" />
<Row
:ratio="ratio"
class="
border
flex
items-center
mt-4
px-2
w-full
rounded-t-md
text-gray-600
dark:border-gray-800 dark:text-gray-400
"
>
<div
v-for="df in tableFields"
:key="df.fieldname"
class="flex items-center px-2 py-2 text-lg"
>
{{ df.label }}
</div>
</Row>
<div
v-if="savedInvoices.length"
class="overflow-y-auto custom-scroll custom-scroll-thumb2"
style="height: 65vh; width: 60vh"
>
<Row
v-for="row in savedInvoices as any"
:key="row.name"
:ratio="ratio"
:border="true"
class="
border-b border-l border-r
dark:border-gray-800 dark:bg-gray-890
flex
group
h-row-mid
hover:bg-gray-25
items-center
justify-center
px-2
w-full
"
@click="$emit('selectedInvoiceName', row)"
>
<FormControl
v-for="df in tableFields"
:key="df.fieldname"
size="large"
:df="df"
:value="row[df.fieldname]"
:read-only="true"
/>
</Row>
</div>
<div class="row-start-6 grid grid-cols-2 gap-4 mt-4">
<div class="col-span-2">
<Button
class="w-full p-5 bg-red-500 dark:bg-red-700"
@click="$emit('toggleModal', 'SavedInvoice')"
>
<slot>
<p class="uppercase text-lg text-white font-semibold">
{{ t`Cancel` }}
</p>
</slot>
</Button>
</div>
</div>
</Modal>
</template>
<script lang="ts">
import Button from 'src/components/Button.vue';
import Modal from 'src/components/Modal.vue';
import Row from 'src/components/Row.vue';
import FormControl from 'src/components/Controls/FormControl.vue';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
import { defineComponent, inject } from 'vue';
import { ModelNameEnum } from 'models/types';
import { Field } from 'schemas/types';
export default defineComponent({
name: 'SavedInvoiceModal',
components: {
Modal,
Button,
FormControl,
Row,
},
props: {
modalStatus: Boolean,
},
emits: ['toggleModal', 'selectedInvoiceName'],
setup() {
return {
sinvDoc: inject('sinvDoc') as SalesInvoice,
};
},
data() {
return {
savedInvoices: [] as SalesInvoice[],
isModalVisible: false,
};
},
computed: {
ratio() {
return [1, 1, 1, 0.8];
},
tableFields() {
return [
{
fieldname: 'name',
label: 'Name',
fieldtype: 'Link',
target: 'SalesInvoice',
readOnly: true,
},
{
fieldname: 'party',
fieldtype: 'Link',
label: 'Customer',
target: 'Party',
placeholder: 'Customer',
readOnly: true,
},
{
fieldname: 'date',
label: 'Date',
fieldtype: 'Date',
readOnly: true,
},
{
fieldname: 'grandTotal',
label: 'Grand Total',
fieldtype: 'Currency',
readOnly: true,
},
] as Field[];
},
},
watch: {
async modalStatus(newVal) {
if (newVal) {
await this.setSavedInvoices();
}
},
},
async mounted() {
await this.setSavedInvoices();
},
async activated() {
await this.setSavedInvoices();
},
methods: {
async setSavedInvoices() {
this.savedInvoices = (await this.fyo.db.getAll(
ModelNameEnum.SalesInvoice,
{
fields: [],
filters: { isPOS: true, submitted: false },
}
)) as SalesInvoice[];
},
async selectedInvoice(row: SalesInvoice) {
let selectedInvoiceDoc = (await this.fyo.doc.getDoc(
ModelNameEnum.SalesInvoice,
row.name
)) as SalesInvoice;
this.sinvDoc = selectedInvoiceDoc;
this.$emit('toggleModal', 'SavedInvoice');
},
},
});
</script>