mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50: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:
parent
28158e5add
commit
f0a6e7bf9e
@ -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 = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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: () => [] },
|
||||
|
@ -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] = '';
|
||||
|
@ -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>
|
||||
|
@ -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>>,
|
||||
|
39
src/pages/CommonForm/LinkedEntries.vue
Normal file
39
src/pages/CommonForm/LinkedEntries.vue
Normal 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>
|
Loading…
Reference in New Issue
Block a user