2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 03:19:01 +00:00

chore: code cleanup

This commit is contained in:
akshayitzme 2023-09-05 17:39:24 +05:30 committed by akshayitzme
parent 7db0cf5ce9
commit 7e7e3ed3cd
7 changed files with 301 additions and 135 deletions

View File

@ -5,13 +5,13 @@ import { OpeningAmounts } from './OpeningAmounts';
import { OpeningCash } from './OpeningCash'; import { OpeningCash } from './OpeningCash';
export class POSShift extends Doc { export class POSShift extends Doc {
isShiftOpen?: boolean;
openingDate?: Date;
closingDate?: Date;
openingAmounts?: OpeningAmounts[];
closingAmounts?: ClosingAmounts[]; closingAmounts?: ClosingAmounts[];
openingCash?: OpeningCash[];
closingCash?: ClosingCash[]; closingCash?: ClosingCash[];
closingDate?: Date;
isShiftOpen?: boolean;
openingAmounts?: OpeningAmounts[];
openingCash?: OpeningCash[];
openingDate?: Date;
get openingCashAmount() { get openingCashAmount() {
if (!this.openingCash) { if (!this.openingCash) {

View File

@ -95,6 +95,7 @@
<div class="px-4 pt-6 col-span-2 flex"> <div class="px-4 pt-6 col-span-2 flex">
<Link <Link
v-if="isUOMConversionEnabled"
:df="{ :df="{
fieldname: 'transferUnit', fieldname: 'transferUnit',
fieldtype: 'Link', fieldtype: 'Link',
@ -108,6 +109,7 @@
@change="(value) => setTransferUnit((row.transferUnit = value))" @change="(value) => setTransferUnit((row.transferUnit = value))"
/> />
<feather-icon <feather-icon
v-if="isUOMConversionEnabled"
name="refresh-ccw" name="refresh-ccw"
class="w-3.5 ml-2 mt-4 text-blue-500" class="w-3.5 ml-2 mt-4 text-blue-500"
@click="row.transferUnit = row.unit" @click="row.transferUnit = row.unit"
@ -116,6 +118,7 @@
<div class="px-4 pt-6 col-span-2"> <div class="px-4 pt-6 col-span-2">
<Int <Int
v-if="isUOMConversionEnabled"
:df="{ :df="{
fieldtype: 'Int', fieldtype: 'Int',
fieldname: 'transferQuantity', fieldname: 'transferQuantity',
@ -130,6 +133,66 @@
/> />
</div> </div>
<div></div>
<div></div>
<div class="px-4 pt-6 flex">
<Currency
:df="{
fieldtype: 'Currency',
fieldname: 'rate',
label: 'Rate',
}"
size="medium"
:show-label="true"
:border="true"
:value="row.rate"
:read-only="false"
@change="(value) => (row.rate = value)"
/>
<feather-icon
name="refresh-ccw"
class="w-3.5 ml-2 mt-5 text-blue-500 flex-none"
@click="row.rate= (defaultRate as Money)"
/>
</div>
<div class="px-6 pt-6 col-span-2">
<Currency
v-if="isDiscountingEnabled"
:df="{
fieldtype: 'Currency',
fieldname: 'discountAmount',
label: 'Discount Amount',
}"
class="col-span-2"
size="medium"
:show-label="true"
:border="true"
:value="row.itemDiscountAmount"
:read-only="row.itemDiscountPercent as number > 0"
@change="(value) => setItemDiscount('amount', value)"
/>
</div>
<div class="px-4 pt-6 col-span-2">
<Float
v-if="isDiscountingEnabled"
:df="{
fieldtype: 'Float',
fieldname: 'itemDiscountPercent',
label: 'Discount Percent',
}"
size="medium"
:show-label="true"
:border="true"
:value="row.itemDiscountPercent"
:read-only="!row.itemDiscountAmount?.isZero()"
@change="(value) => setItemDiscount('percent', value)"
/>
</div>
<div class=""></div>
<div <div
v-if="row.links && row.links?.item.hasBatch" v-if="row.links && row.links?.item.hasBatch"
class="pl-6 px-4 pt-6 col-span-2" class="pl-6 px-4 pt-6 col-span-2"
@ -185,63 +248,6 @@
@change="(value:string)=> setSerialNumber(value)" @change="(value:string)=> setSerialNumber(value)"
/> />
</div> </div>
<div class=""></div>
<div class="px-4 pt-6 col-span-2 flex">
<Currency
:df="{
fieldtype: 'Currency',
fieldname: 'rate',
label: 'Rate',
}"
class="col-span-2 flex-1"
size="medium"
:show-label="true"
:border="true"
:value="row.rate"
:read-only="false"
@change="(value) => (row.rate = value)"
/>
<feather-icon
name="refresh-ccw"
class="w-3.5 ml-2 mt-5 text-blue-500"
@click="row.rate= (defaultRate as Money)"
/>
</div>
<div class="px-6 pt-6 col-span-2">
<Currency
:df="{
fieldtype: 'Currency',
fieldname: 'discountAmount',
label: 'Discount Amount',
}"
class="col-span-2"
size="medium"
:show-label="true"
:border="true"
:value="row.itemDiscountAmount"
:read-only="row.itemDiscountPercent as number > 0"
@change="(value) => setItemDiscount('amount', value)"
/>
</div>
<div class="px-4 pt-6">
<Float
:df="{
fieldtype: 'Float',
fieldname: 'itemDiscountPercent',
label: 'Discount Percent',
}"
size="medium"
:show-label="true"
:border="true"
:value="row.itemDiscountPercent"
:read-only="!row.itemDiscountAmount?.isZero()"
@change="(value) => setItemDiscount('percent', value)"
/>
</div>
</template> </template>
</template> </template>
@ -268,6 +274,7 @@ export default defineComponent({
emits: ['removeItem', 'setItemSerialNumbers'], emits: ['removeItem', 'setItemSerialNumbers'],
setup() { setup() {
return { return {
isDiscountingEnabled: inject('isDiscountingEnabled') as boolean,
itemSerialNumbers: inject('itemSerialNumbers') as { itemSerialNumbers: inject('itemSerialNumbers') as {
[item: string]: string; [item: string]: string;
}, },
@ -282,6 +289,11 @@ export default defineComponent({
defaultRate: this.row.rate as Money, defaultRate: this.row.rate as Money,
}; };
}, },
computed: {
isUOMConversionEnabled(): boolean {
return !!fyo.singles.InventorySettings?.enableUomConversions;
},
},
methods: { methods: {
async getAvailableQtyInBatch(): Promise<number> { async getAvailableQtyInBatch(): Promise<number> {
if (!this.row.batch) { if (!this.row.batch) {

View File

@ -42,19 +42,31 @@
</Row> </Row>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import FormContainer from '../FormContainer.vue';
import FormControl from '../Controls/FormControl.vue';
import Link from '../Controls/Link.vue';
import Row from '../Row.vue'; import Row from '../Row.vue';
import RowEditForm from 'src/pages/CommonForm/RowEditForm.vue';
import SelectedItemRow from './SelectedItemRow.vue'; import SelectedItemRow from './SelectedItemRow.vue';
import { isNumeric } from 'src/utils'; import { isNumeric } from 'src/utils';
import { inject } from 'vue'; import { inject } from 'vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { Field } from 'schemas/types';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem'; import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
import { Field } from 'schemas/types';
export default defineComponent({ export default defineComponent({
name: 'SelectedItemTable', name: 'SelectedItemTable',
components: { Row, SelectedItemRow }, components: {
FormContainer,
FormControl,
Link,
Row,
RowEditForm,
SelectedItemRow,
},
setup() { setup() {
return { return {
sinvDoc: inject('sinvDoc') as SalesInvoice, sinvDoc: inject('sinvDoc') as SalesInvoice,

View File

@ -1,5 +1,5 @@
<template> <template>
<Modal class="w-3/6 p-4"> <Modal :open-modal="openModal" class="w-3/6 p-4">
<h1 class="text-xl font-semibold text-center pb-4">Close POS Shift</h1> <h1 class="text-xl font-semibold text-center pb-4">Close POS Shift</h1>
<h2 class="mt-4 mb-2 text-lg font-medium">Closing Cash</h2> <h2 class="mt-4 mb-2 text-lg font-medium">Closing Cash</h2>
@ -50,25 +50,37 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { OpeningAmounts } from 'models/inventory/Point of Sale/OpeningAmounts'; import Button from 'src/components/Button.vue';
import { POSShift } from 'models/inventory/Point of Sale/POSShift'; import Modal from 'src/components/Modal.vue';
import Table from 'src/components/Controls/Table.vue';
import { ModelNameEnum } from 'models/types'; import { ModelNameEnum } from 'models/types';
import { Money } from 'pesa'; import { Money } from 'pesa';
import Button from 'src/components/Button.vue'; import { OpeningAmounts } from 'models/inventory/Point of Sale/OpeningAmounts';
import Table from 'src/components/Controls/Table.vue'; import { POSShift } from 'models/inventory/Point of Sale/POSShift';
import Modal from 'src/components/Modal.vue';
import { fyo } from 'src/initFyo';
import { computed } from 'vue'; import { computed } from 'vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { fyo } from 'src/initFyo';
import { showToast } from 'src/utils/interactive';
import { t } from 'fyo';
import {
validateClosingAmounts,
transferPOSCashAndWriteOff,
} from 'src/utils/pos';
export default defineComponent({ export default defineComponent({
name: 'ClosePOSShiftModal', name: 'ClosePOSShiftModal',
components: { Modal, Table, Button }, components: { Button, Modal, Table },
provide() { provide() {
return { return {
doc: computed(() => this.posShiftDoc), doc: computed(() => this.posShiftDoc),
}; };
}, },
props: {
openModal: {
default: false,
type: Boolean,
},
},
emits: ['toggleModal'], emits: ['toggleModal'],
data() { data() {
return { return {
@ -78,13 +90,25 @@ export default defineComponent({
transactedAmount: {} as Record<string, Money> | undefined, transactedAmount: {} as Record<string, Money> | undefined,
}; };
}, },
async mounted() { watch: {
openModal: {
async handler() {
await this.setTransactedAmount();
await this.seedClosingAmounts();
},
},
},
async activated() {
this.posShiftDoc = fyo.singles[ModelNameEnum.POSShift]; this.posShiftDoc = fyo.singles[ModelNameEnum.POSShift];
await this.seedValues(); await this.seedValues();
await this.setTransactedAmount();
}, },
methods: { methods: {
async setTransactedAmount() { async setTransactedAmount() {
const fromDate = fyo.singles.POSShift?.openingDate as Date; if (!fyo.singles.POSShift?.openingDate) {
return;
}
const fromDate = fyo.singles.POSShift?.openingDate;
this.transactedAmount = await fyo.db.getPOSTransactedAmount(fromDate); this.transactedAmount = await fyo.db.getPOSTransactedAmount(fromDate);
}, },
seedClosingCash() { seedClosingCash() {
@ -106,8 +130,6 @@ export default defineComponent({
return; return;
} }
await this.setTransactedAmount();
this.posShiftDoc.closingAmounts = []; this.posShiftDoc.closingAmounts = [];
await this.posShiftDoc.sync(); await this.posShiftDoc.sync();
@ -155,30 +177,28 @@ export default defineComponent({
await this.seedClosingAmounts(); await this.seedClosingAmounts();
this.isValuesSeeded = true; this.isValuesSeeded = true;
}, },
setClosingCashAmount() {
if (!this.posShiftDoc?.closingAmounts) {
return;
}
this.posShiftDoc.closingAmounts.map((row) => {
if (row.paymentMethod === 'Cash') {
row.closingAmount = this.posShiftDoc?.closingCashAmount;
}
});
},
getField(fieldname: string) { getField(fieldname: string) {
return this.fyo.getField(ModelNameEnum.POSShift, fieldname); return fyo.getField(ModelNameEnum.POSShift, fieldname);
}, },
async handleChange() { async handleChange() {
await this.posShiftDoc?.sync(); await this.posShiftDoc?.sync();
this.setClosingCashAmount();
}, },
async handleSubmit() { async handleSubmit() {
this.$emit('toggleModal', 'ShiftClose'); try {
await this.posShiftDoc?.setMultiple({ validateClosingAmounts(this.posShiftDoc as POSShift);
isShiftOpen: false,
}); await this.posShiftDoc?.set('isShiftOpen', false);
await this.posShiftDoc?.sync(); await this.posShiftDoc?.sync();
await transferPOSCashAndWriteOff(fyo, this.posShiftDoc as POSShift);
this.$emit('toggleModal', 'ShiftClose');
} catch (error) {
return showToast({
type: 'error',
message: t`${error as string}`,
duration: 'short',
});
}
}, },
}, },
}); });

View File

@ -33,10 +33,10 @@
/> />
<div class="mt-4 grid grid-cols-2 gap-4 flex items-end"> <div class="mt-4 grid grid-cols-2 gap-4 flex items-end">
<Button class="w-full py-5 bg-red-500" @click="seedDefaults"> <Button class="w-full py-5 bg-red-500" @click="$router.back()">
<slot> <slot>
<p class="uppercase text-lg text-white font-semibold"> <p class="uppercase text-lg text-white font-semibold">
{{ t`Clear` }} {{ t`Back` }}
</p> </p>
</slot> </slot>
</Button> </Button>
@ -58,12 +58,16 @@
import Button from 'src/components/Button.vue'; import Button from 'src/components/Button.vue';
import Modal from 'src/components/Modal.vue'; import Modal from 'src/components/Modal.vue';
import Table from 'src/components/Controls/Table.vue'; import Table from 'src/components/Controls/Table.vue';
import { fyo } from 'src/initFyo'; import { AccountTypeEnum } from 'models/baseModels/Account/types';
import { defineComponent } from 'vue';
import { computed } from 'vue';
import { POSShift } from 'models/inventory/Point of Sale/POSShift';
import { Money } from 'pesa';
import { ModelNameEnum } from 'models/types'; import { ModelNameEnum } from 'models/types';
import { Money } from 'pesa';
import { POSShift } from 'models/inventory/Point of Sale/POSShift';
import { computed } from 'vue';
import { defineComponent } from 'vue';
import { fyo } from 'src/initFyo';
import { showToast } from 'src/utils/interactive';
import { t } from 'fyo';
import { ValidationError } from 'fyo/utils/errors';
export default defineComponent({ export default defineComponent({
name: 'OpenPOSShift', name: 'OpenPOSShift',
@ -85,6 +89,12 @@ export default defineComponent({
getDefaultCashDenominations() { getDefaultCashDenominations() {
return this.fyo.singles.Defaults?.posCashDenominations; return this.fyo.singles.Defaults?.posCashDenominations;
}, },
posCashAccount() {
return fyo.singles.POSSettings?.cashAccount;
},
posOpeningCashAmount(): Money {
return this.posShiftDoc?.openingCashAmount as Money;
},
}, },
async mounted() { async mounted() {
this.isValuesSeeded = false; this.isValuesSeeded = false;
@ -164,13 +174,49 @@ export default defineComponent({
this.setOpeningCashAmount(); this.setOpeningCashAmount();
}, },
async handleSubmit() { async handleSubmit() {
try {
if (this.posShiftDoc?.openingCashAmount.isNegative()) {
throw new ValidationError(
t`Opening Cash Amount can not be negative.`
);
}
await this.posShiftDoc?.setMultiple({ await this.posShiftDoc?.setMultiple({
isShiftOpen: true, isShiftOpen: true,
openingDate: new Date(), openingDate: new Date(),
}); });
await this.posShiftDoc?.sync(); await this.posShiftDoc?.sync();
if (!this.posShiftDoc?.openingCashAmount.isZero()) {
const jvDoc = fyo.doc.getNewDoc(ModelNameEnum.JournalEntry, {
entryType: 'Journal Entry',
});
await jvDoc.append('accounts', {
account: this.posCashAccount,
debit: this.posShiftDoc?.openingCashAmount as Money,
credit: this.fyo.pesa(0),
});
await jvDoc.append('accounts', {
account: AccountTypeEnum.Cash,
debit: this.fyo.pesa(0),
credit: this.posShiftDoc?.openingCashAmount as Money,
});
await (await jvDoc.sync()).submit();
}
this.$emit('toggleModal', 'ShiftOpen'); this.$emit('toggleModal', 'ShiftOpen');
} catch (error) {
showToast({
type: 'error',
message: t`${error as string}`,
duration: 'short',
});
return;
}
}, },
}, },
}); });

View File

@ -153,6 +153,7 @@
/> />
<Currency <Currency
v-if="isDiscountingEnabled"
:df="{ :df="{
label: t`Discount Amount`, label: t`Discount Amount`,
fieldtype: 'Currency', fieldtype: 'Currency',
@ -256,6 +257,7 @@ export default defineComponent({
setup() { setup() {
return { return {
cashAmount: inject('cashAmount') as Money, cashAmount: inject('cashAmount') as Money,
isDiscountingEnabled: inject('isDiscountingEnabled') as boolean,
itemDiscounts: inject('itemDiscounts') as Money, itemDiscounts: inject('itemDiscounts') as Money,
transferAmount: inject('transferAmount') as Money, transferAmount: inject('transferAmount') as Money,
sinvDoc: inject('sinvDoc') as SalesInvoice, sinvDoc: inject('sinvDoc') as SalesInvoice,
@ -264,23 +266,22 @@ export default defineComponent({
}; };
}, },
computed: { computed: {
showPaidChange(): boolean { balanceAmount(): Money {
if ( const grandTotal = this.sinvDoc?.grandTotal ?? fyo.pesa(0);
this.cashAmount.eq(fyo.pesa(0)) &&
this.transferAmount.eq(fyo.pesa(0)) if (this.cashAmount.isZero()) {
) { return grandTotal.sub(this.transferAmount);
return false;
} }
if (this.cashAmount.lt(this.sinvDoc?.grandTotal ?? fyo.pesa(0))) { if (this.transferAmount.isZero()) {
return false; return grandTotal.sub(this.cashAmount);
} }
if (this.transferAmount.gte(this.sinvDoc?.grandTotal ?? fyo.pesa(0))) { const totalPaidAmount = this.cashAmount.add(this.transferAmount);
return false; return grandTotal.sub(totalPaidAmount);
} },
paidChange(): Money {
return true; return this.cashAmount.sub(this.sinvDoc?.grandTotal ?? fyo.pesa(0));
}, },
showBalanceAmount(): boolean { showBalanceAmount(): boolean {
if ( if (
@ -300,22 +301,23 @@ export default defineComponent({
return true; return true;
}, },
paidChange(): Money { showPaidChange(): boolean {
return this.cashAmount.sub(this.sinvDoc?.grandTotal ?? fyo.pesa(0)); if (
}, this.cashAmount.eq(fyo.pesa(0)) &&
balanceAmount(): Money { this.transferAmount.eq(fyo.pesa(0))
const grandTotal = this.sinvDoc?.grandTotal ?? fyo.pesa(0); ) {
return false;
if (this.cashAmount.isZero()) {
return grandTotal.sub(this.transferAmount);
} }
if (this.transferAmount.isZero()) { if (this.cashAmount.lt(this.sinvDoc?.grandTotal ?? fyo.pesa(0))) {
return grandTotal.sub(this.cashAmount); return false;
} }
const totalPaidAmount = this.cashAmount.add(this.transferAmount); if (this.transferAmount.gte(this.sinvDoc?.grandTotal ?? fyo.pesa(0))) {
return grandTotal.sub(totalPaidAmount); return false;
}
return true;
}, },
}, },
methods: { methods: {

View File

@ -1,8 +1,10 @@
import { t } from 'fyo'; import { Fyo, t } from 'fyo';
import { ValidationError } from 'fyo/utils/errors'; import { ValidationError } from 'fyo/utils/errors';
import { AccountTypeEnum } from 'models/baseModels/Account/types';
import { Item } from 'models/baseModels/Item/Item'; import { Item } from 'models/baseModels/Item/Item';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice'; import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem'; import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
import { POSShift } from 'models/inventory/Point of Sale/POSShift';
import { ValuationMethod } from 'models/inventory/types'; import { ValuationMethod } from 'models/inventory/types';
import { ModelNameEnum } from 'models/types'; import { ModelNameEnum } from 'models/types';
import { Money } from 'pesa'; import { Money } from 'pesa';
@ -22,8 +24,11 @@ export async function getItemQtyMap(): Promise<ItemQtyMap> {
const rawSLEs = await getRawStockLedgerEntries(fyo); const rawSLEs = await getRawStockLedgerEntries(fyo);
const rawData = getStockLedgerEntries(rawSLEs, valuationMethod); const rawData = getStockLedgerEntries(rawSLEs, valuationMethod);
const posInventory = fyo.singles.POSSettings?.inventory;
const stockBalance = getStockBalanceEntries(rawData, {}); const stockBalance = getStockBalanceEntries(rawData, {
location: posInventory,
});
for (const row of stockBalance) { for (const row of stockBalance) {
if (!itemQtyMap[row.item]) { if (!itemQtyMap[row.item]) {
@ -147,3 +152,72 @@ export function getTotalTaxedAmount(sinvDoc: SalesInvoice): Money {
} }
return totalTaxedAmount; return totalTaxedAmount;
} }
export function validateClosingAmounts(posShiftDoc: POSShift) {
if (!posShiftDoc) {
throw new ValidationError(`POS Shift Document not loaded. Please reload.`);
}
posShiftDoc.closingAmounts?.map((row) => {
if (row.closingAmount?.isNegative()) {
throw new ValidationError(
t`Closing ${row.paymentMethod as string} Amount can not be negative.`
);
}
});
}
export async function transferPOSCashAndWriteOff(
fyo: Fyo,
posShiftDoc: POSShift
) {
const closingCashAmount = posShiftDoc.closingAmounts?.find(
(row) => row.paymentMethod === 'Cash'
)?.closingAmount as Money;
const jvDoc = fyo.doc.getNewDoc(ModelNameEnum.JournalEntry, {
entryType: 'Journal Entry',
});
await jvDoc.append('accounts', {
account: AccountTypeEnum.Cash,
debit: closingCashAmount,
});
await jvDoc.append('accounts', {
account: fyo.singles.POSSettings?.cashAccount,
credit: closingCashAmount,
});
const differenceAmount = posShiftDoc?.closingAmounts?.find(
(row) => row.paymentMethod === 'Cash'
)?.differenceAmount as Money;
if (differenceAmount.isNegative()) {
await jvDoc.append('accounts', {
account: AccountTypeEnum.Cash,
debit: differenceAmount.abs(),
credit: fyo.pesa(0),
});
await jvDoc.append('accounts', {
account: fyo.singles.POSSettings?.writeOffAccount,
debit: fyo.pesa(0),
credit: differenceAmount.abs(),
});
}
if (!differenceAmount.isZero() && differenceAmount.isPositive()) {
await jvDoc.append('accounts', {
account: fyo.singles.POSSettings?.writeOffAccount,
debit: differenceAmount,
credit: fyo.pesa(0),
});
await jvDoc.append('accounts', {
account: AccountTypeEnum.Cash,
debit: fyo.pesa(0),
credit: differenceAmount,
});
}
await (await jvDoc.sync()).submit();
}