2
0
mirror of https://github.com/frappe/books.git synced 2024-11-08 14:50:56 +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';
export class POSShift extends Doc {
isShiftOpen?: boolean;
openingDate?: Date;
closingDate?: Date;
openingAmounts?: OpeningAmounts[];
closingAmounts?: ClosingAmounts[];
openingCash?: OpeningCash[];
closingCash?: ClosingCash[];
closingDate?: Date;
isShiftOpen?: boolean;
openingAmounts?: OpeningAmounts[];
openingCash?: OpeningCash[];
openingDate?: Date;
get openingCashAmount() {
if (!this.openingCash) {

View File

@ -95,6 +95,7 @@
<div class="px-4 pt-6 col-span-2 flex">
<Link
v-if="isUOMConversionEnabled"
:df="{
fieldname: 'transferUnit',
fieldtype: 'Link',
@ -108,6 +109,7 @@
@change="(value) => setTransferUnit((row.transferUnit = value))"
/>
<feather-icon
v-if="isUOMConversionEnabled"
name="refresh-ccw"
class="w-3.5 ml-2 mt-4 text-blue-500"
@click="row.transferUnit = row.unit"
@ -116,6 +118,7 @@
<div class="px-4 pt-6 col-span-2">
<Int
v-if="isUOMConversionEnabled"
:df="{
fieldtype: 'Int',
fieldname: 'transferQuantity',
@ -130,6 +133,66 @@
/>
</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
v-if="row.links && row.links?.item.hasBatch"
class="pl-6 px-4 pt-6 col-span-2"
@ -185,63 +248,6 @@
@change="(value:string)=> setSerialNumber(value)"
/>
</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>
@ -268,6 +274,7 @@ export default defineComponent({
emits: ['removeItem', 'setItemSerialNumbers'],
setup() {
return {
isDiscountingEnabled: inject('isDiscountingEnabled') as boolean,
itemSerialNumbers: inject('itemSerialNumbers') as {
[item: string]: string;
},
@ -282,6 +289,11 @@ export default defineComponent({
defaultRate: this.row.rate as Money,
};
},
computed: {
isUOMConversionEnabled(): boolean {
return !!fyo.singles.InventorySettings?.enableUomConversions;
},
},
methods: {
async getAvailableQtyInBatch(): Promise<number> {
if (!this.row.batch) {

View File

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

View File

@ -1,5 +1,5 @@
<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>
<h2 class="mt-4 mb-2 text-lg font-medium">Closing Cash</h2>
@ -50,25 +50,37 @@
</template>
<script lang="ts">
import { OpeningAmounts } from 'models/inventory/Point of Sale/OpeningAmounts';
import { POSShift } from 'models/inventory/Point of Sale/POSShift';
import Button from 'src/components/Button.vue';
import Modal from 'src/components/Modal.vue';
import Table from 'src/components/Controls/Table.vue';
import { ModelNameEnum } from 'models/types';
import { Money } from 'pesa';
import Button from 'src/components/Button.vue';
import Table from 'src/components/Controls/Table.vue';
import Modal from 'src/components/Modal.vue';
import { fyo } from 'src/initFyo';
import { OpeningAmounts } from 'models/inventory/Point of Sale/OpeningAmounts';
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 {
validateClosingAmounts,
transferPOSCashAndWriteOff,
} from 'src/utils/pos';
export default defineComponent({
name: 'ClosePOSShiftModal',
components: { Modal, Table, Button },
components: { Button, Modal, Table },
provide() {
return {
doc: computed(() => this.posShiftDoc),
};
},
props: {
openModal: {
default: false,
type: Boolean,
},
},
emits: ['toggleModal'],
data() {
return {
@ -78,13 +90,25 @@ export default defineComponent({
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];
await this.seedValues();
await this.setTransactedAmount();
},
methods: {
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);
},
seedClosingCash() {
@ -106,8 +130,6 @@ export default defineComponent({
return;
}
await this.setTransactedAmount();
this.posShiftDoc.closingAmounts = [];
await this.posShiftDoc.sync();
@ -155,30 +177,28 @@ export default defineComponent({
await this.seedClosingAmounts();
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) {
return this.fyo.getField(ModelNameEnum.POSShift, fieldname);
return fyo.getField(ModelNameEnum.POSShift, fieldname);
},
async handleChange() {
await this.posShiftDoc?.sync();
this.setClosingCashAmount();
},
async handleSubmit() {
this.$emit('toggleModal', 'ShiftClose');
await this.posShiftDoc?.setMultiple({
isShiftOpen: false,
});
try {
validateClosingAmounts(this.posShiftDoc as POSShift);
await this.posShiftDoc?.set('isShiftOpen', false);
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">
<Button class="w-full py-5 bg-red-500" @click="seedDefaults">
<Button class="w-full py-5 bg-red-500" @click="$router.back()">
<slot>
<p class="uppercase text-lg text-white font-semibold">
{{ t`Clear` }}
{{ t`Back` }}
</p>
</slot>
</Button>
@ -58,12 +58,16 @@
import Button from 'src/components/Button.vue';
import Modal from 'src/components/Modal.vue';
import Table from 'src/components/Controls/Table.vue';
import { fyo } from 'src/initFyo';
import { defineComponent } from 'vue';
import { computed } from 'vue';
import { POSShift } from 'models/inventory/Point of Sale/POSShift';
import { Money } from 'pesa';
import { AccountTypeEnum } from 'models/baseModels/Account/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({
name: 'OpenPOSShift',
@ -85,6 +89,12 @@ export default defineComponent({
getDefaultCashDenominations() {
return this.fyo.singles.Defaults?.posCashDenominations;
},
posCashAccount() {
return fyo.singles.POSSettings?.cashAccount;
},
posOpeningCashAmount(): Money {
return this.posShiftDoc?.openingCashAmount as Money;
},
},
async mounted() {
this.isValuesSeeded = false;
@ -164,13 +174,49 @@ export default defineComponent({
this.setOpeningCashAmount();
},
async handleSubmit() {
try {
if (this.posShiftDoc?.openingCashAmount.isNegative()) {
throw new ValidationError(
t`Opening Cash Amount can not be negative.`
);
}
await this.posShiftDoc?.setMultiple({
isShiftOpen: true,
openingDate: new Date(),
});
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');
} catch (error) {
showToast({
type: 'error',
message: t`${error as string}`,
duration: 'short',
});
return;
}
},
},
});

View File

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

View File

@ -1,8 +1,10 @@
import { t } from 'fyo';
import { Fyo, t } from 'fyo';
import { ValidationError } from 'fyo/utils/errors';
import { AccountTypeEnum } from 'models/baseModels/Account/types';
import { Item } from 'models/baseModels/Item/Item';
import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice';
import { SalesInvoiceItem } from 'models/baseModels/SalesInvoiceItem/SalesInvoiceItem';
import { POSShift } from 'models/inventory/Point of Sale/POSShift';
import { ValuationMethod } from 'models/inventory/types';
import { ModelNameEnum } from 'models/types';
import { Money } from 'pesa';
@ -22,8 +24,11 @@ export async function getItemQtyMap(): Promise<ItemQtyMap> {
const rawSLEs = await getRawStockLedgerEntries(fyo);
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) {
if (!itemQtyMap[row.item]) {
@ -147,3 +152,72 @@ export function getTotalTaxedAmount(sinvDoc: SalesInvoice): Money {
}
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();
}