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:
commit
6fb81a56d7
@ -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,
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
186
src/pages/POS/SavedInvoiceModal.vue
Normal file
186
src/pages/POS/SavedInvoiceModal.vue
Normal 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>
|
Loading…
Reference in New Issue
Block a user