2
0
mirror of https://github.com/frappe/books.git synced 2024-11-08 23:00:56 +00:00

fix(ui): propagate row changes

- type ExchangeRate.vue
- add BarCode and ExchangeRate widgets to CommonForm
- create (not complete) with LinkedEntries.vue
This commit is contained in:
18alantom 2023-04-13 11:46:51 +05:30
parent 28158e5add
commit f0a6e7bf9e
7 changed files with 185 additions and 28 deletions

View File

@ -194,7 +194,6 @@ export abstract class Invoice extends Transactional {
account: string;
rate: number;
amount: Money;
[key: string]: DocValue;
}
> = {};
@ -223,11 +222,23 @@ export abstract class Invoice extends Transactional {
}
}
return Object.keys(taxes)
.map((account) => {
return taxes[account];
})
.filter((tax) => !tax.amount.isZero());
type Summary = typeof taxes[string] & { idx: number };
const taxArr: Summary[] = [];
let idx = 0;
for (const account in taxes) {
const tax = taxes[account];
if (tax.amount.isZero()) {
continue;
}
taxArr.push({
...tax,
idx,
});
idx += 1;
}
return taxArr;
}
async getTax(tax: string) {
@ -419,6 +430,19 @@ export abstract class Invoice extends Transactional {
discountPercent: () =>
true || !(this.enableDiscounting && !this.setDiscountAmount),
discountAfterTax: () => !this.enableDiscounting,
taxes: () => !this.taxes?.length,
baseGrandTotal: () =>
this.exchangeRate === 1 || this.baseGrandTotal!.isZero(),
grandTotal: () => !this.taxes?.length,
entryCurrency: () => !this.isMultiCurrency,
currency: () => !this.isMultiCurrency,
exchangeRate: () => !this.isMultiCurrency,
stockNotTransferred: () => !this.stockNotTransferred,
outstandingAmount: () =>
!!this.outstandingAmount?.isZero() || !this.isSubmitted,
terms: () => !(this.terms || !(this.isSubmitted || this.isCancelled)),
attachment: () =>
!(this.attachment || !(this.isSubmitted || this.isCancelled)),
};
static defaults: DefaultMap = {

View File

@ -16,7 +16,6 @@
>
<input
type="number"
ref="toValue"
:value="isSwapped ? fromValue / exchangeRate : exchangeRate * fromValue"
:disabled="disabled"
min="0"
@ -34,7 +33,7 @@
</button>
</div>
</template>
<script>
<script lang="ts">
import { safeParseFloat } from 'utils/index';
import { defineComponent } from 'vue';
@ -53,9 +52,9 @@ export default defineComponent({
swap() {
this.isSwapped = !this.isSwapped;
},
rightChange(e) {
let value = this.$refs.toValue.value;
if (e) {
rightChange(e: Event) {
let value: string | number = 1;
if (e.target instanceof HTMLInputElement) {
value = e.target.value;
}
@ -70,14 +69,14 @@ export default defineComponent({
},
},
computed: {
left() {
left(): string {
if (this.isSwapped) {
return this.toCurrency;
}
return this.fromCurrency;
},
right() {
right(): string {
if (this.isSwapped) {
return this.fromCurrency;
}

View File

@ -39,8 +39,9 @@
:key="row.name"
v-bind="{ row, tableFields, size, ratio, isNumeric }"
:read-only="isReadOnly"
@remove="removeRow(row)"
:can-edit-row="canEditRow"
@remove="removeRow(row)"
@change="(field, value) => $emit('row-change', field, value, this.df)"
/>
</div>
@ -95,7 +96,7 @@ import TableRow from './TableRow.vue';
export default {
name: 'Table',
emits: ['editrow'],
emits: ['editrow', 'row-change'],
extends: Base,
props: {
value: { type: Array, default: () => [] },

View File

@ -1,16 +1,8 @@
<template>
<Row
:ratio="ratio"
class="
w-full
px-2
hover:bg-gray-25
group
flex
items-center
justify-center
h-row-mid
"
class="w-full px-2 group flex items-center justify-center h-row-mid"
:class="readOnly ? '' : 'hover:bg-gray-25'"
>
<!-- Index or Remove button -->
<div class="flex items-center ps-2 text-gray-600">
@ -78,7 +70,7 @@ export default {
default: false,
},
},
emits: ['remove'],
emits: ['remove', 'change'],
components: {
Row,
FormControl,
@ -106,6 +98,7 @@ export default {
try {
await this.row.set(fieldname, value);
this.$emit('change', df, value);
} catch (e) {
this.errors[fieldname] = getErrorMessage(e, this.row);
this.row[fieldname] = '';

View File

@ -2,15 +2,43 @@
<FormContainer>
<template #header-left v-if="hasDoc">
<StatusBadge :status="status" class="h-8" />
<Barcode
class="h-8"
v-if="canShowBarcode"
@item-selected="(name:string) => {
// @ts-ignore
doc?.addItem(name);
}"
/>
<ExchangeRate
v-if="hasDoc && doc.isMultiCurrency"
:disabled="doc?.isSubmitted || doc?.isCancelled"
:from-currency="fromCurrency"
:to-currency="toCurrency"
:exchange-rate="exchangeRate"
@change="
async (exchangeRate: number) =>
await doc.set('exchangeRate', exchangeRate)
"
/>
</template>
<template #header v-if="hasDoc">
<Button
v-if="canShowLinks"
:icon="true"
@click="showLinks = true"
:title="t`View linked entries`"
>
<feather-icon name="link" class="w-4 h-4"></feather-icon>
</Button>
<Button
v-if="canPrint"
ref="printButton"
:icon="true"
@click="routeTo(`/print/${doc.schemaName}/${doc.name}`)"
:title="t`Open Print View`"
>
{{ t`Print` }}
<feather-icon name="printer" class="w-4 h-4"></feather-icon>
</Button>
<DropdownWithActions
v-for="group of groupedActions"
@ -53,6 +81,7 @@
:doc="doc"
:errors="errors"
@value-change="onValueChange"
@row-change="updateGroupedFields"
/>
</div>
@ -105,6 +134,13 @@
@close="() => toggleQuickEditDoc(null)"
/>
</Transition>
<Transition name="quickedit">
<LinkedEntries
v-if="showLinks && !hasQeDoc"
:doc="doc"
@close="showLinks = false"
/>
</Transition>
</template>
</FormContainer>
</template>
@ -140,6 +176,10 @@ import { inject } from 'vue';
import { shortcutsKey } from 'src/utils/injectionKeys';
import { ref } from 'vue';
import { useDocShortcuts } from 'src/utils/vueUtils';
import Barcode from 'src/components/Controls/Barcode.vue';
import { DEFAULT_CURRENCY } from 'fyo/utils/consts';
import ExchangeRate from 'src/components/Controls/ExchangeRate.vue';
import LinkedEntries from './LinkedEntries.vue';
export default defineComponent({
props: {
@ -173,12 +213,14 @@ export default defineComponent({
groupedFields: null,
quickEditDoc: null,
isPrintable: false,
showLinks: false,
} as {
errors: Record<string, string>;
activeTab: string;
groupedFields: null | UIGroupedFields;
quickEditDoc: null | Doc;
isPrintable: boolean;
showLinks: boolean;
};
},
async mounted() {
@ -208,6 +250,45 @@ export default defineComponent({
docsPathRef.value = '';
},
computed: {
canShowBarcode(): boolean {
if (!this.fyo.singles.InventorySettings?.enableBarcodes) {
return false;
}
if (!this.hasDoc) {
return false;
}
if (this.doc.isSubmitted || this.doc.isCancelled) {
return false;
}
// @ts-ignore
return typeof this.doc?.addItem === 'function';
},
exchangeRate(): number {
if (!this.hasDoc || typeof this.doc.exchangeRate !== 'number') {
return 1;
}
return this.doc.exchangeRate;
},
fromCurrency(): string {
const currency = this.doc?.currency;
if (typeof currency !== 'string') {
return this.toCurrency;
}
return currency;
},
toCurrency(): string {
const currency = this.fyo.singles.SystemSettings?.currency;
if (typeof currency !== 'string') {
return DEFAULT_CURRENCY;
}
return currency;
},
canPrint(): boolean {
if (!this.hasDoc) {
return false;
@ -215,6 +296,17 @@ export default defineComponent({
return !this.doc.isCancelled && !this.doc.dirty && this.isPrintable;
},
canShowLinks(): boolean {
if (!this.hasDoc || this.hasQeDoc || this.showLinks) {
return false;
}
if (this.doc.schema.isSubmittable && !this.doc.isSubmitted) {
return false;
}
return this.doc.inserted;
},
hasDoc(): boolean {
return this.docOrNull instanceof Doc;
},
@ -321,6 +413,11 @@ export default defineComponent({
await nextTick();
}
if (doc && this.showLinks) {
this.showLinks = false;
await nextTick();
}
this.quickEditDoc = doc;
},
async onValueChange(field: Field, value: DocValue) {
@ -348,6 +445,9 @@ export default defineComponent({
Button,
DropdownWithActions,
QuickEditForm,
Barcode,
ExchangeRate,
LinkedEntries,
},
});
</script>

View File

@ -32,6 +32,7 @@
:value="doc[field.fieldname]"
@editrow="(doc: Doc) => $emit('editrow', doc)"
@change="(value: DocValue) => $emit('value-change', field, value)"
@row-change="(field:Field, value:DocValue, parentfield:Field) => $emit('row-change',field, value, parentfield)"
/>
<div v-if="errors?.[field.fieldname]" class="text-sm text-red-600 mt-1">
{{ errors[field.fieldname] }}
@ -49,7 +50,7 @@ import { focusOrSelectFormControl } from 'src/utils/ui';
import { defineComponent, PropType } from 'vue';
export default defineComponent({
emits: ['editrow', 'value-change'],
emits: ['editrow', 'value-change', 'row-change'],
props: {
title: String,
errors: Object as PropType<Record<string, string>>,

View File

@ -0,0 +1,39 @@
<template>
<div class="w-quick-edit bg-white border-l">
<div
class="
flex
items-center
justify-between
px-4
h-row-largest
sticky
top-0
border-b
"
style="z-index: 1"
>
<div class="flex items-center justify-between w-full">
<Button :icon="true" @click="$emit('close')">
<feather-icon name="x" class="w-4 h-4" />
</Button>
<p class="text-xl font-semibold text-gray-600">
{{ t`Linked Entries` }}
</p>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Doc } from 'fyo/model/doc';
import Button from 'src/components/Button.vue';
import { defineComponent } from 'vue';
import { PropType } from 'vue';
export default defineComponent({
emits: ['close'],
props: { doc: { type: Object as PropType<Doc>, required: true } },
mounted() {},
components: { Button },
});
</script>