Skip to content

Commit

Permalink
feat: #151 Add quote object and allow create invoice from it
Browse files Browse the repository at this point in the history
Also allow changing a print template type, necessary to create a custom
print template for this.
  • Loading branch information
mildred committed Dec 22, 2023
1 parent b4041f5 commit 3c1a3b5
Show file tree
Hide file tree
Showing 23 changed files with 318 additions and 12 deletions.
2 changes: 2 additions & 0 deletions backend/patches/updateSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const defaultNumberSeriesMap = {
[ModelNameEnum.JournalEntry]: 'JV-',
[ModelNameEnum.SalesInvoice]: 'SINV-',
[ModelNameEnum.PurchaseInvoice]: 'PINV-',
[ModelNameEnum.SalesQuote]: 'SQUOT-',
} as Record<ModelNameEnum, string>;

async function execute(dm: DatabaseManager) {
Expand Down Expand Up @@ -209,6 +210,7 @@ async function copyTransactionalTables(
ModelNameEnum.Payment,
ModelNameEnum.SalesInvoice,
ModelNameEnum.PurchaseInvoice,
ModelNameEnum.SalesQuote,
];

for (const sn of schemaNames) {
Expand Down
7 changes: 7 additions & 0 deletions models/baseModels/Defaults/Defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export class Defaults extends Doc {
purchaseReceiptLocation?: string;

// Number Series
salesQuoteNumberSeries?: string;
salesInvoiceNumberSeries?: string;
purchaseInvoiceNumberSeries?: string;
journalEntryNumberSeries?: string;
Expand All @@ -29,6 +30,7 @@ export class Defaults extends Doc {
purchaseReceiptTerms?: string;

// Print Templates
salesQuotePrintTemplate?: string;
salesInvoicePrintTemplate?: string;
purchaseInvoicePrintTemplate?: string;
journalEntryPrintTemplate?: string;
Expand All @@ -46,6 +48,9 @@ export class Defaults extends Doc {
salesPaymentAccount: () => ({ isGroup: false, accountType: 'Cash' }),
purchasePaymentAccount: () => ({ isGroup: false, accountType: 'Cash' }),
// Number Series
salesQuoteNumberSeries: () => ({
referenceType: ModelNameEnum.SalesQuote,
}),
salesInvoiceNumberSeries: () => ({
referenceType: ModelNameEnum.SalesInvoice,
}),
Expand All @@ -68,6 +73,7 @@ export class Defaults extends Doc {
referenceType: ModelNameEnum.PurchaseReceipt,
}),
// Print Templates
salesQuotePrintTemplate: () => ({ type: ModelNameEnum.SalesQuote }),
salesInvoicePrintTemplate: () => ({ type: ModelNameEnum.SalesInvoice }),
purchaseInvoicePrintTemplate: () => ({
type: ModelNameEnum.PurchaseInvoice,
Expand Down Expand Up @@ -118,4 +124,5 @@ export const numberSeriesDefaultsMap: Record<
[ModelNameEnum.StockMovement]: 'stockMovementNumberSeries',
[ModelNameEnum.Shipment]: 'shipmentNumberSeries',
[ModelNameEnum.PurchaseReceipt]: 'purchaseReceiptNumberSeries',
[ModelNameEnum.SalesQuote]: 'salesQuoteNumberSeries',
};
11 changes: 8 additions & 3 deletions models/baseModels/Invoice/Invoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ export abstract class Invoice extends Transactional {
returnAgainst?: string;

get isSales() {
return this.schemaName === 'SalesInvoice';
return this.schemaName === 'SalesInvoice' || this.schemaName == 'SalesQuote';
}

get isQuote() {
return this.schemaName == 'SalesQuote';
}

get enableDiscounting() {
Expand Down Expand Up @@ -490,7 +494,7 @@ export abstract class Invoice extends Transactional {
}

async _updateIsItemsReturned() {
if (!this.isReturn || !this.returnAgainst) {
if (!this.isReturn || !this.returnAgainst || this.isQuote) {
return;
}

Expand All @@ -512,7 +516,7 @@ export abstract class Invoice extends Transactional {
}

async _validateHasLinkedReturnInvoices() {
if (!this.name || this.isReturn) {
if (!this.name || this.isReturn || this.isQuote) {
return;
}

Expand Down Expand Up @@ -682,6 +686,7 @@ export abstract class Invoice extends Transactional {
attachment: () =>
!(this.attachment || !(this.isSubmitted || this.isCancelled)),
backReference: () => !this.backReference,
quote: () => !this.quote,
priceList: () => !this.fyo.singles.AccountingSettings?.enablePriceList,
returnAgainst: () =>
(this.isSubmitted || this.isCancelled) && !this.returnAgainst,
Expand Down
2 changes: 1 addition & 1 deletion models/baseModels/InvoiceItem/InvoiceItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export abstract class InvoiceItem extends Doc {
itemTaxedTotal?: Money;

get isSales() {
return this.schemaName === 'SalesInvoiceItem';
return this.schemaName === 'SalesInvoiceItem' || this.schemaName === 'SalesQuoteItem';
}

get date() {
Expand Down
1 change: 1 addition & 0 deletions models/baseModels/PrintTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export class PrintTemplate extends Doc {

const models = [
ModelNameEnum.SalesInvoice,
ModelNameEnum.SalesQuote,
ModelNameEnum.PurchaseInvoice,
ModelNameEnum.JournalEntry,
ModelNameEnum.Payment,
Expand Down
65 changes: 65 additions & 0 deletions models/baseModels/SalesQuote/SalesQuote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Fyo } from 'fyo';
import { Action, ListViewSettings } from 'fyo/model/types';
import { LedgerPosting } from 'models/Transactional/LedgerPosting';
import { ModelNameEnum } from 'models/types';
import { getQuoteActions, getTransactionStatusColumn } from '../../helpers';
import { Invoice } from '../Invoice/Invoice';
import { SalesQuoteItem } from '../SalesQuoteItem/SalesQuoteItem';

export class SalesQuote extends Invoice {
items?: SalesQuoteItem[];

async getPosting() {
return null;
}

async getInvoice(): Promise<Invoice | null> {
if (!this.isSubmitted) {
return null;
}

const schemaName = ModelNameEnum.SalesInvoice;
const defaults = (this.fyo.singles.Defaults as Defaults) ?? {};
const terms = defaults.salesInvoiceTerms ?? '';
const numberSeries = defaults.salesInvoiceNumberSeries ?? undefined;

const data = {
...this,
date: new Date().toISOString(),
terms,
numberSeries,
quote: this.name,
items: []
};

const invoice = this.fyo.doc.getNewDoc(schemaName, data) as Invoice;
for (const row of this.items ?? []) {
await invoice.append('items', {
...row
});
}

if (!invoice.items?.length) {
return null;
}

return invoice;
}

static getListViewSettings(): ListViewSettings {
return {
columns: [
'name',
getTransactionStatusColumn(),
'party',
'date',
'baseGrandTotal',
'outstandingAmount',
],
};
}

static getActions(fyo: Fyo): Action[] {
return getQuoteActions(fyo, ModelNameEnum.SalesQuote);
}
}
3 changes: 3 additions & 0 deletions models/baseModels/SalesQuoteItem/SalesQuoteItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { InvoiceItem } from '../InvoiceItem/InvoiceItem';

export class SalesQuoteItem extends InvoiceItem {}
21 changes: 18 additions & 3 deletions models/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ import { StockMovement } from './inventory/StockMovement';
import { StockTransfer } from './inventory/StockTransfer';
import { InvoiceStatus, ModelNameEnum } from './types';

export function getQuoteActions(
fyo: Fyo,
schemaName: ModelNameEnum.SalesQuote
): Action[] {
return [
getMakeInvoiceAction(fyo, schemaName),
];
}

export function getInvoiceActions(
fyo: Fyo,
schemaName: ModelNameEnum.SalesInvoice | ModelNameEnum.PurchaseInvoice
Expand Down Expand Up @@ -67,7 +76,7 @@ export function getMakeStockTransferAction(

export function getMakeInvoiceAction(
fyo: Fyo,
schemaName: ModelNameEnum.Shipment | ModelNameEnum.PurchaseReceipt
schemaName: ModelNameEnum.Shipment | ModelNameEnum.PurchaseReceipt | ModelNameEnum.SalesQuote
): Action {
let label = fyo.t`Sales Invoice`;
if (schemaName === ModelNameEnum.PurchaseReceipt) {
Expand All @@ -77,9 +86,15 @@ export function getMakeInvoiceAction(
return {
label,
group: fyo.t`Create`,
condition: (doc: Doc) => doc.isSubmitted && !doc.backReference,
condition: (doc: Doc) => {
if (schemaName === ModelNameEnum.SalesQuote) {
return doc.isSubmitted
} else {
return doc.isSubmitted && !doc.backReference
}
},
action: async (doc: Doc) => {
const invoice = await (doc as StockTransfer).getInvoice();
let invoice = await (doc as SalesQuote | StockTransfer).getInvoice();
if (!invoice || !invoice.name) {
return;
}
Expand Down
4 changes: 4 additions & 0 deletions models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { PurchaseInvoice } from './baseModels/PurchaseInvoice/PurchaseInvoice';
import { PurchaseInvoiceItem } from './baseModels/PurchaseInvoiceItem/PurchaseInvoiceItem';
import { SalesInvoice } from './baseModels/SalesInvoice/SalesInvoice';
import { SalesInvoiceItem } from './baseModels/SalesInvoiceItem/SalesInvoiceItem';
import { SalesQuote } from './baseModels/SalesQuote/SalesQuote';
import { SalesQuoteItem } from './baseModels/SalesQuoteItem/SalesQuoteItem';
import { SetupWizard } from './baseModels/SetupWizard/SetupWizard';
import { Tax } from './baseModels/Tax/Tax';
import { TaxSummary } from './baseModels/TaxSummary/TaxSummary';
Expand Down Expand Up @@ -61,6 +63,8 @@ export const models = {
PurchaseInvoiceItem,
SalesInvoice,
SalesInvoiceItem,
SalesQuote,
SalesQuoteItem,
SerialNumber,
SetupWizard,
PrintTemplate,
Expand Down
2 changes: 2 additions & 0 deletions models/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export enum ModelNameEnum {
PurchaseInvoiceItem = 'PurchaseInvoiceItem',
SalesInvoice = 'SalesInvoice',
SalesInvoiceItem = 'SalesInvoiceItem',
SalesQuote = 'SalesQuote',
SalesQuoteItem = 'SalesQuoteItem',
SerialNumber = 'SerialNumber',
SetupWizard = 'SetupWizard',
Tax = 'Tax',
Expand Down
15 changes: 15 additions & 0 deletions schemas/app/Defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@
"create": true,
"section": "Number Series"
},
{
"fieldname": "salesQuoteNumberSeries",
"label": "Sales Quote Number Series",
"fieldtype": "Link",
"target": "NumberSeries",
"create": true,
"section": "Number Series"
},
{
"fieldname": "salesInvoiceTerms",
"label": "Sales Invoice Terms",
Expand All @@ -116,6 +124,13 @@
"fieldtype": "Text",
"section": "Terms"
},
{
"fieldname": "salesQuotePrintTemplate",
"label": "Sales Quote Print Template",
"fieldtype": "Link",
"target": "PrintTemplate",
"section": "Print Templates"
},
{
"fieldname": "salesInvoicePrintTemplate",
"label": "Sales Invoice Print Template",
Expand Down
4 changes: 4 additions & 0 deletions schemas/app/NumberSeries.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
"value": "SalesInvoice",
"label": "Sales Invoice"
},
{
"value": "SalesQuote",
"label": "Sales Quote"
},
{
"value": "PurchaseInvoice",
"label": "Purchase Invoice"
Expand Down
8 changes: 8 additions & 0 deletions schemas/app/SalesInvoice.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@
"target": "Shipment",
"section": "References"
},
{
"fieldname": "quote",
"label": "Quote Reference",
"fieldtype": "Link",
"target": "SalesQuote",
"section": "References",
"required": false
},
{
"fieldname": "makeAutoStockTransfer",
"label": "Make Shipment On Submit",
Expand Down
58 changes: 58 additions & 0 deletions schemas/app/SalesQuote.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"name": "SalesQuote",
"label": "Quote",
"extends": "Invoice",
"naming": "numberSeries",
"showTitle": true,
"fields": [
{
"fieldname": "numberSeries",
"label": "Number Series",
"fieldtype": "Link",
"target": "NumberSeries",
"create": true,
"required": true,
"default": "SQUOT-",
"section": "Default"
},
{
"fieldname": "account",
"drop": true
},
{
"fieldname": "stockNotTransferred",
"drop": true
},
{
"fieldname": "backReference",
"drop": true
},
{
"fieldname": "makeAutoStockTransfer",
"drop": true
},
{
"fieldname": "returnAgainst",
"drop": true
},
{
"fieldname": "party",
"label": "Customer",
"fieldtype": "Link",
"target": "Party",
"create": true,
"required": true,
"section": "Default"
},
{
"fieldname": "items",
"label": "Items",
"fieldtype": "Table",
"target": "SalesQuoteItem",
"required": true,
"edit": true,
"section": "Items"
}
],
"keywordFields": ["name", "party"]
}
5 changes: 5 additions & 0 deletions schemas/app/SalesQuoteItem.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "SalesQuoteItem",
"label": "Sales Quote Item",
"extends": "InvoiceItem"
}
6 changes: 5 additions & 1 deletion schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,11 @@ function getCombined(
const combined = Object.assign(abstractSchema, extendingSchema);

for (const fieldname in extendingFields) {
abstractFields[fieldname] = extendingFields[fieldname];
if (extendingFields[fieldname].drop) {
delete abstractFields[fieldname]
} else {
abstractFields[fieldname] = extendingFields[fieldname];
}
}

combined.fields = getListFromMap(abstractFields);
Expand Down
Loading

0 comments on commit 3c1a3b5

Please sign in to comment.