diff --git a/fyo/model/types.ts b/fyo/model/types.ts index 35184f18..df103d86 100644 --- a/fyo/model/types.ts +++ b/fyo/model/types.ts @@ -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; } + +export type DocStatus = + | '' + | 'Draft' + | 'Saved' + | 'NotSaved' + | 'Submitted' + | 'Cancelled'; diff --git a/fyo/utils/index.ts b/fyo/utils/index.ts index 647960e7..d7f07075 100644 --- a/fyo/utils/index.ts +++ b/fyo/utils/index.ts @@ -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'; diff --git a/models/baseModels/JournalEntry/JournalEntry.ts b/models/baseModels/JournalEntry/JournalEntry.ts index 1aeedf34..2e191e63 100644 --- a/models/baseModels/JournalEntry/JournalEntry.ts +++ b/models/baseModels/JournalEntry/JournalEntry.ts @@ -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: `${status}`, + template: `${label}`, }; }, }, diff --git a/models/baseModels/Payment/Payment.ts b/models/baseModels/Payment/Payment.ts index 00f847c7..a32d944e 100644 --- a/models/baseModels/Payment/Payment.ts +++ b/models/baseModels/Payment/Payment.ts @@ -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: `${status}`, + template: `${label}`, }; }, }, diff --git a/models/helpers.ts b/models/helpers.ts index d8632d38..e2d7c618 100644 --- a/models/helpers.ts +++ b/models/helpers.ts @@ -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: `${label}`, }; @@ -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 { + 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({ diff --git a/models/types.ts b/models/types.ts index 6cb25110..972e56ea 100644 --- a/models/types.ts +++ b/models/types.ts @@ -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 \ No newline at end of file +export type ModelName = keyof typeof ModelNameEnum; diff --git a/src/components/StatusBadge.vue b/src/components/StatusBadge.vue index 5fb54447..f8b47635 100644 --- a/src/components/StatusBadge.vue +++ b/src/components/StatusBadge.vue @@ -4,8 +4,7 @@ }}