2
0
mirror of https://github.com/frappe/books.git synced 2025-02-02 12:08:27 +00:00

fix(ux): improve status indication

This commit is contained in:
18alantom 2022-06-14 14:40:46 +05:30
parent d5b881c98f
commit daebdb0ea9
13 changed files with 160 additions and 93 deletions

View File

@ -1,6 +1,6 @@
import { DocValue, DocValueMap } from 'fyo/core/types';
import SystemSettings from 'fyo/models/SystemSettings';
import { FieldType, SelectOption } from 'schemas/types';
import { FieldType, Schema, SelectOption } from 'schemas/types';
import { QueryFilter } from 'utils/db/types';
import { Router } from 'vue-router';
import { Doc } from './doc';
@ -69,12 +69,17 @@ export interface Action {
};
}
export interface RenderData {
schema: Schema,
[key: string]: DocValue | Schema
}
export interface ColumnConfig {
label: string;
fieldtype: FieldType;
fieldname?: string;
size?: string;
render?: (doc: Doc) => { template: string };
render?: (doc: RenderData) => { template: string };
getValue?: (doc: Doc) => string;
}
@ -88,3 +93,11 @@ export interface TreeViewSettings {
parentField: string;
getRootLabel: () => Promise<string>;
}
export type DocStatus =
| ''
| 'Draft'
| 'Saved'
| 'NotSaved'
| 'Submitted'
| 'Cancelled';

View File

@ -1,6 +1,6 @@
import { Fyo } from 'fyo';
import { Doc } from 'fyo/model/doc';
import { Action } from 'fyo/model/types';
import { Action, DocStatus } from 'fyo/model/types';
import { Money } from 'pesa';
import { Field, OptionField, SelectOption } from 'schemas/types';
import { getIsNullOrUndef } from 'utils';

View File

@ -7,7 +7,12 @@ import {
ListViewSettings,
} from 'fyo/model/types';
import { DateTime } from 'luxon';
import { getLedgerLinkAction } from 'models/helpers';
import {
getDocStatus,
getLedgerLinkAction,
getStatusMap,
statusColor,
} from 'models/helpers';
import { Transactional } from 'models/Transactional/Transactional';
import { Money } from 'pesa';
import { LedgerPosting } from '../../Transactional/LedgerPosting';
@ -55,20 +60,12 @@ export class JournalEntry extends Transactional {
fieldtype: 'Select',
size: 'small',
render(doc) {
let status = 'Draft';
let color = 'gray';
if (doc.submitted) {
color = 'green';
status = 'Submitted';
}
if (doc.cancelled) {
color = 'red';
status = 'Cancelled';
}
const status = getDocStatus(doc);
const color = statusColor[status] ?? 'gray';
const label = getStatusMap()[status];
return {
template: `<Badge class="text-xs" color="${color}">${status}</Badge>`,
template: `<Badge class="text-xs" color="${color}">${label}</Badge>`,
};
},
},

View File

@ -13,7 +13,12 @@ import {
ValidationMap,
} from 'fyo/model/types';
import { ValidationError } from 'fyo/utils/errors';
import { getLedgerLinkAction } from 'models/helpers';
import {
getDocStatus,
getLedgerLinkAction,
getStatusMap,
statusColor,
} from 'models/helpers';
import { LedgerPosting } from 'models/Transactional/LedgerPosting';
import { Transactional } from 'models/Transactional/Transactional';
import { ModelNameEnum } from 'models/types';
@ -449,19 +454,12 @@ export class Payment extends Transactional {
fieldtype: 'Select',
size: 'small',
render(doc) {
let status = 'Draft';
let color = 'gray';
if (doc.submitted) {
color = 'green';
status = 'Submitted';
}
if (doc.cancelled) {
color = 'red';
status = 'Cancelled';
}
const status = getDocStatus(doc);
const color = statusColor[status] ?? 'gray';
const label = getStatusMap()[status];
return {
template: `<Badge class="text-xs" color="${color}">${status}</Badge>`,
template: `<Badge class="text-xs" color="${color}">${label}</Badge>`,
};
},
},

View File

@ -1,6 +1,6 @@
import { Fyo, t } from 'fyo';
import { Doc } from 'fyo/model/doc';
import { Action, ColumnConfig } from 'fyo/model/types';
import { Action, ColumnConfig, DocStatus, RenderData } from 'fyo/model/types';
import { NotFoundError } from 'fyo/utils/errors';
import { DateTime } from 'luxon';
import { Money } from 'pesa';
@ -76,21 +76,17 @@ export function getLedgerLinkAction(fyo: Fyo): Action {
}
export function getTransactionStatusColumn(): ColumnConfig {
const statusMap = {
Unpaid: t`Unpaid`,
Paid: t`Paid`,
Draft: t`Draft`,
Cancelled: t`Cancelled`,
};
const statusMap = getStatusMap();
return {
label: t`Status`,
fieldname: 'status',
fieldtype: 'Select',
render(doc: Doc) {
const status = getInvoiceStatus(doc) as InvoiceStatus;
render(doc) {
const status = getDocStatus(doc) as InvoiceStatus;
const color = statusColor[status];
const label = statusMap[status];
return {
template: `<Badge class="text-xs" color="${color}">${label}</Badge>`,
};
@ -98,27 +94,97 @@ export function getTransactionStatusColumn(): ColumnConfig {
};
}
export const statusColor = {
export const statusColor: Record<
DocStatus | InvoiceStatus,
string | undefined
> = {
'': 'gray',
Draft: 'gray',
Unpaid: 'orange',
Paid: 'green',
Saved: 'gray',
NotSaved: 'gray',
Submitted: 'green',
Cancelled: 'red',
};
export function getInvoiceStatus(doc: Doc) {
let status = 'Unpaid';
if (!doc.submitted) {
status = 'Draft';
export function getStatusMap(): Record<DocStatus | InvoiceStatus, string> {
return {
'': '',
Draft: t`Draft`,
Unpaid: t`Unpaid`,
Paid: t`Paid`,
Saved: t`Saved`,
NotSaved: t`Not Saved`,
Submitted: t`Submitted`,
Cancelled: t`Cancelled`,
};
}
export function getDocStatus(
doc?: RenderData | Doc
): DocStatus | InvoiceStatus {
if (!doc) {
return '';
}
if (doc.submitted && (doc.outstandingAmount as Money).isZero()) {
status = 'Paid';
if (doc.notInserted) {
return 'Draft';
}
if (doc.dirty) {
return 'NotSaved';
}
if (!doc.schema?.isSubmittable) {
return 'Saved';
}
return getSubmittableDocStatus(doc);
}
function getSubmittableDocStatus(doc: RenderData | Doc) {
if (
[ModelNameEnum.SalesInvoice, ModelNameEnum.PurchaseInvoice].includes(
doc.schema.name as ModelNameEnum
)
) {
return getInvoiceStatus(doc);
}
if (!!doc.submitted && !doc.cancelled) {
return 'Submitted';
}
if (!!doc.submitted && !!doc.cancelled) {
return 'Cancelled';
}
return 'Saved';
}
export function getInvoiceStatus(doc: RenderData | Doc): InvoiceStatus {
if (
doc.submitted &&
!doc.cancelled &&
(doc.outstandingAmount as Money).isZero()
) {
return 'Paid';
}
if (
doc.submitted &&
!doc.cancelled &&
(doc.outstandingAmount as Money).isPositive()
) {
return 'Unpaid';
}
if (doc.cancelled) {
status = 'Cancelled';
return 'Cancelled';
}
return status;
return 'Saved';
}
export async function getExchangeRate({

View File

@ -1,4 +1,4 @@
export type InvoiceStatus = 'Draft' | 'Unpaid' | 'Cancelled' | 'Paid';
export type InvoiceStatus = 'Draft' | 'Saved' | 'Unpaid' | 'Cancelled' | 'Paid';
export enum ModelNameEnum {
Account = 'Account',
AccountingLedgerEntry = 'AccountingLedgerEntry',
@ -29,4 +29,4 @@ export enum ModelNameEnum {
SystemSettings = 'SystemSettings',
}
export type ModelName = keyof typeof ModelNameEnum
export type ModelName = keyof typeof ModelNameEnum;

View File

@ -4,8 +4,7 @@
}}</Badge>
</template>
<script>
import { t } from 'fyo';
import { statusColor } from 'src/utils/colors';
import { getStatusMap, statusColor } from 'models/helpers';
import Badge from './Badge.vue';
export default {
@ -16,14 +15,8 @@ export default {
return statusColor[this.status];
},
statusLabel() {
return (
{
Draft: t`Draft`,
Unpaid: t`Unpaid`,
Paid: t`Paid`,
Cancelled: t`Cancelled`,
}[this.status] ?? this.status
);
const statusMap = getStatusMap();
return statusMap[this.status] ?? this.status;
},
},
components: { Badge },

View File

@ -179,7 +179,7 @@
</template>
<script>
import { computed } from '@vue/reactivity';
import { getInvoiceStatus } from 'models/helpers';
import { getDocStatus } from 'models/helpers';
import { ModelNameEnum } from 'models/types';
import Button from 'src/components/Button.vue';
import FormControl from 'src/components/Controls/FormControl.vue';
@ -189,10 +189,10 @@ import FormContainer from 'src/components/FormContainer.vue';
import StatusBadge from 'src/components/StatusBadge.vue';
import { fyo } from 'src/initFyo';
import {
getActionsForDocument,
openSettings,
routeTo,
showMessageDialog
getActionsForDocument,
openSettings,
routeTo,
showMessageDialog,
} from 'src/utils/ui';
import { handleErrorWithDialog } from '../errorHandling';
@ -224,15 +224,15 @@ export default {
};
},
updated() {
this.chstatus = !this.chstatus
this.chstatus = !this.chstatus;
},
computed: {
address() {
return this.printSettings && this.printSettings.getLink('address');
},
status() {
this.chstatus
return getInvoiceStatus(this.doc);
this.chstatus;
return getDocStatus(this.doc);
},
},
async mounted() {

View File

@ -24,7 +24,17 @@
<!-- Journal Entry Form -->
<template #body v-if="doc">
<div class="px-4 text-xl font-semibold flex justify-between h-row-large items-center">
<div
class="
px-4
text-xl
font-semibold
flex
justify-between
h-row-large
items-center
"
>
<h1>
{{ doc.notInserted ? t`New Entry` : doc.name }}
</h1>
@ -130,6 +140,7 @@
</template>
<script>
import { computed } from '@vue/reactivity';
import { getDocStatus } from 'models/helpers';
import { ModelNameEnum } from 'models/types';
import Button from 'src/components/Button.vue';
import FormControl from 'src/components/Controls/FormControl.vue';
@ -187,11 +198,7 @@ export default {
},
computed: {
status() {
if (this.doc.notInserted || !this.doc.submitted) {
return 'Draft';
}
return '';
return getDocStatus(this.doc);
},
totalDebit() {
let value = 0;

View File

@ -189,11 +189,13 @@ export default defineComponent({
? 'date'
: 'created';
this.data = await fyo.db.getAll(this.schemaName, {
fields: ['*'],
filters,
orderBy,
});
this.data = (
await fyo.db.getAll(this.schemaName, {
fields: ['*'],
filters,
orderBy,
})
).map((d) => ({ ...d, schema: fyo.schemaMap[this.schemaName] }));
},
},
});

View File

@ -16,15 +16,15 @@
</div>
<!-- Actions, Badge and Status Change Buttons -->
<div class="flex items-stretch">
<DropdownWithActions :actions="actions" />
<div class="flex items-stretch gap-2">
<StatusBadge :status="status" />
<DropdownWithActions :actions="actions" />
<Button
:icon="true"
@click="sync"
type="primary"
v-if="doc?.dirty || doc?.notInserted"
class="ml-2 text-white text-xs"
class="text-white text-xs"
>
{{ t`Save` }}
</Button>
@ -38,7 +38,7 @@
!doc?.notInserted &&
!(doc?.cancelled || false)
"
class="ml-2 text-white text-xs"
class="text-white text-xs"
>
{{ t`Submit` }}
</Button>
@ -89,6 +89,7 @@
<script>
import { computed } from '@vue/reactivity';
import { t } from 'fyo';
import { getDocStatus } from 'models/helpers';
import Button from 'src/components/Button.vue';
import FormControl from 'src/components/Controls/FormControl.vue';
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
@ -148,11 +149,7 @@ export default {
return fyo.schemaMap[this.schemaName] ?? null;
},
status() {
if (this.doc && this.doc.notInserted) {
return 'Draft';
}
return '';
return getDocStatus(this.doc);
},
fields() {
if (!this.schema) {

View File

@ -75,7 +75,7 @@ export default defineComponent({
}
if (filterKeys.length) {
await this.report.postSet();
await this.report.updateData();
}
if (fyo.store.isDevelopment) {

View File

@ -14,12 +14,6 @@ export const indicators = {
BLACK: 'black',
};
export const statusColor = {
Draft: 'gray',
Unpaid: 'orange',
Paid: 'green',
Cancelled: 'red',
};
const getValidColor = (color: string) => {
const isValid = [