2
0
mirror of https://github.com/frappe/books.git synced 2024-09-19 19:19:02 +00:00

incr: redo duplication (don't sync)

- fix journal entry
This commit is contained in:
18alantom 2022-05-04 18:17:45 +05:30
parent fefc79024d
commit eedb4415ce
9 changed files with 141 additions and 57 deletions

View File

@ -15,7 +15,11 @@ import {
SchemaMap,
TargetField,
} from '../../schemas/types';
import { getRandomString, getValueMapFromList } from '../../utils';
import {
getIsNullOrUndef,
getRandomString,
getValueMapFromList,
} from '../../utils';
import { DatabaseBase, GetAllOptions, QueryFilter } from '../../utils/db/types';
import { getDefaultMetaFieldValueMap, sqliteTypeMap, SYSTEM } from '../helpers';
import {
@ -889,12 +893,13 @@ export default class DatabaseCore extends DatabaseBase {
const tableFieldValue = fieldValueMap[field.fieldname] as
| FieldValueMap[]
| undefined;
if (tableFieldValue === undefined) {
| undefined
| null;
if (getIsNullOrUndef(tableFieldValue)) {
continue;
}
for (const child of tableFieldValue) {
for (const child of tableFieldValue!) {
this.#prepareChild(schemaName, parentName, child, field, added.length);
if (

View File

@ -127,6 +127,10 @@ export class Doc extends Observable<DocValue | Doc[]> {
} else {
this[fieldname] = value ?? this[fieldname] ?? null;
}
if (field.fieldtype === FieldTypeEnum.Table && !this[fieldname]) {
this[fieldname] = [];
}
}
}
@ -269,6 +273,10 @@ export class Doc extends Observable<DocValue | Doc[]> {
}
_getChildDoc(docValueMap: Doc | DocValueMap, fieldname: string): Doc {
if (!this.name) {
this.name = getRandomString();
}
docValueMap.name ??= getRandomString();
// Child Meta Fields
@ -304,6 +312,10 @@ export class Doc extends Observable<DocValue | Doc[]> {
for (const field of tableFields) {
const childDocs = this.get(field.fieldname) as Doc[];
if (!childDocs) {
continue;
}
checkForMandatory.push(...childDocs);
}
@ -351,13 +363,18 @@ export class Doc extends Observable<DocValue | Doc[]> {
await validator(value);
}
getValidDict(): DocValueMap {
getValidDict(filterMeta: boolean = false): DocValueMap {
let fields = this.schema.fields;
if (filterMeta) {
fields = this.schema.fields.filter((f) => !f.meta);
}
const data: DocValueMap = {};
for (const field of this.schema.fields) {
for (const field of fields) {
let value = this[field.fieldname] as DocValue | DocValueMap[];
if (Array.isArray(value)) {
value = value.map((doc) => (doc as Doc).getValidDict());
value = value.map((doc) => (doc as Doc).getValidDict(filterMeta));
}
if (isPesa(value)) {
@ -704,25 +721,17 @@ export class Doc extends Observable<DocValue | Doc[]> {
return await this.sync();
}
async duplicate(shouldSync: boolean = true): Promise<Doc> {
const updateMap: DocValueMap = {};
const docValueMap = this.getValidDict();
const fieldnames = this.schema.fields.map((f) => f.fieldname);
for (const fn of fieldnames) {
const value = docValueMap[fn];
if (getIsNullOrUndef(value)) {
duplicate(): Doc {
const updateMap = this.getValidDict(true);
for (const field in updateMap) {
const value = updateMap[field];
if (!Array.isArray(value)) {
continue;
}
if (Array.isArray(value)) {
value.forEach((row) => {
for (const row of value) {
delete row.name;
delete row.parent;
});
}
updateMap[fn] = value;
}
if (this.numberSeries) {
@ -731,12 +740,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
updateMap.name = updateMap.name + ' CPY';
}
const doc = this.fyo.doc.getNewDoc(this.schemaName, updateMap, false);
if (shouldSync) {
await doc.sync();
}
return doc;
return this.fyo.doc.getNewDoc(this.schemaName, updateMap);
}
/**

View File

@ -26,7 +26,8 @@ export class LedgerPosting {
fyo: Fyo;
refDoc: Transactional;
entries: AccountingLedgerEntry[];
entryMap: Record<string, AccountingLedgerEntry>;
creditMap: Record<string, AccountingLedgerEntry>;
debitMap: Record<string, AccountingLedgerEntry>;
reverted: boolean;
accountBalanceChanges: AccountBalanceChange[];
@ -34,19 +35,20 @@ export class LedgerPosting {
this.fyo = fyo;
this.refDoc = refDoc;
this.entries = [];
this.entryMap = {};
this.creditMap = {};
this.debitMap = {};
this.reverted = false;
this.accountBalanceChanges = [];
}
async debit(account: string, amount: Money) {
const ledgerEntry = this._getLedgerEntry(account);
const ledgerEntry = this._getLedgerEntry(account, 'debit');
await ledgerEntry.set('debit', ledgerEntry.debit!.add(amount));
await this._updateAccountBalanceChange(account, 'debit', amount);
}
async credit(account: string, amount: Money) {
const ledgerEntry = this._getLedgerEntry(account);
const ledgerEntry = this._getLedgerEntry(account, 'credit');
await ledgerEntry.set('credit', ledgerEntry.credit!.add(amount));
await this._updateAccountBalanceChange(account, 'credit', amount);
}
@ -101,9 +103,17 @@ export class LedgerPosting {
});
}
_getLedgerEntry(account: string): AccountingLedgerEntry {
if (this.entryMap[account]) {
return this.entryMap[account];
_getLedgerEntry(
account: string,
type: TransactionType
): AccountingLedgerEntry {
let map = this.creditMap;
if (type === 'debit') {
map = this.debitMap;
}
if (map[account]) {
return map[account];
}
const ledgerEntry = this.fyo.doc.getNewDoc(
@ -122,9 +132,9 @@ export class LedgerPosting {
) as AccountingLedgerEntry;
this.entries.push(ledgerEntry);
this.entryMap[account] = ledgerEntry;
map[account] = ledgerEntry;
return this.entryMap[account];
return map[account];
}
_validateIsEqual() {

View File

@ -13,12 +13,12 @@ import Money from 'pesa/dist/types/src/money';
import { LedgerPosting } from '../../Transactional/LedgerPosting';
export class JournalEntry extends Transactional {
accounts: Doc[] = [];
accounts?: Doc[];
async getPosting() {
const posting: LedgerPosting = new LedgerPosting(this, this.fyo);
for (const row of this.accounts) {
for (const row of this.accounts ?? []) {
const debit = row.debit as Money;
const credit = row.credit as Money;
const account = row.account as string;

View File

@ -11,9 +11,11 @@ import {
validateEmail,
validatePhoneNumber,
} from 'fyo/model/validationFunction';
import Money from 'pesa/dist/types/src/money';
import { PartyRole } from './types';
export class Party extends Doc {
outstandingAmount?: Money;
async updateOutstandingAmount() {
/**
* If Role === "Both" then outstanding Amount

View File

@ -73,7 +73,7 @@ export class Payment extends Transactional {
updateAmountOnReferenceUpdate() {
this.amount = this.fyo.pesa(0);
for (const paymentReference of this.for as Doc[]) {
for (const paymentReference of (this.for ?? []) as Doc[]) {
this.amount = (this.amount as Money).add(
paymentReference.amount as Money
);
@ -163,16 +163,28 @@ export class Payment extends Transactional {
}
async getPosting() {
const account = this.account as string;
const paymentAccount = this.paymentAccount as string;
const amount = this.amount as Money;
const writeoff = this.writeoff as Money;
/**
* account : From Account
* paymentAccount : To Account
*
* if Receive
* - account : Debtors, etc
* - paymentAccount : Cash, Bank, etc
*
* if Pay
* - account : Cash, Bank, etc
* - paymentAccount : Creditors, etc
*/
const posting: LedgerPosting = new LedgerPosting(this, this.fyo);
await posting.debit(paymentAccount as string, amount.sub(writeoff));
await posting.credit(account as string, amount.sub(writeoff));
const paymentAccount = this.paymentAccount as string;
const account = this.account as string;
const amount = this.amount as Money;
this.applyWriteOffPosting(posting);
await posting.debit(paymentAccount as string, amount);
await posting.credit(account as string, amount);
await this.applyWriteOffPosting(posting);
return posting;
}
@ -183,11 +195,12 @@ export class Payment extends Transactional {
}
const account = this.account as string;
const paymentAccount = this.paymentAccount as string;
const writeOffAccount = this.fyo.singles.AccountingSettings!
.writeOffAccount as string;
if (this.paymentType === 'Pay') {
await posting.credit(account, writeoff);
await posting.credit(paymentAccount, writeoff);
await posting.debit(writeOffAccount, writeoff);
} else {
await posting.debit(account, writeoff);
@ -277,7 +290,7 @@ export class Payment extends Transactional {
}
async _revertReferenceOutstanding() {
for (const ref of this.for as PaymentFor[]) {
for (const ref of (this.for ?? []) as PaymentFor[]) {
const refDoc = await this.fyo.doc.getDoc(
ref.referenceType!,
ref.referenceName!
@ -318,6 +331,32 @@ export class Payment extends Transactional {
},
dependsOn: ['paymentMethod', 'paymentType'],
},
paymentType: {
formula: async () => {
if (!this.party) {
return;
}
const partyDoc = await this.fyo.doc.getDoc(
ModelNameEnum.Party,
this.party
);
if (partyDoc.role === 'Supplier') {
return 'Pay';
} else if (partyDoc.role === 'Customer') {
return 'Receive';
}
const outstanding = partyDoc.outstandingAmount as Money;
if (outstanding?.isZero() ?? true) {
return '';
}
if (outstanding?.isPositive()) {
return 'Receive';
}
return 'Pay';
},
},
amount: {
formula: async () => this.getSum('for', 'amount', false),
dependsOn: ['for'],
@ -332,7 +371,10 @@ export class Payment extends Transactional {
);
}
if ((this.for as Doc[]).length === 0) return;
if (((this.for ?? []) as Doc[]).length === 0) {
return;
}
const amount = this.getSum('for', 'amount', false);
if ((value as Money).gt(amount)) {

View File

@ -113,7 +113,7 @@
},
{
"fieldname": "writeoff",
"label": "Write Off / Refund",
"label": "Write Off",
"fieldtype": "Currency"
},
{

View File

@ -21,7 +21,11 @@
</Row>
<!-- Data Rows -->
<div class="overflow-auto" :style="{ 'max-height': maxHeight }">
<div
class="overflow-auto"
:style="{ 'max-height': maxHeight }"
v-if="value"
>
<TableRow
:class="{ 'pointer-events-none': isReadOnly }"
ref="table-row"
@ -58,7 +62,9 @@
'px-2 py-3': size === 'small',
'px-3 py-4': size !== 'small',
}"
v-if="maxRowsBeforeOverflow && value.length > maxRowsBeforeOverflow"
v-if="
value && maxRowsBeforeOverflow && value.length > maxRowsBeforeOverflow
"
>
{{ t`${value.length} rows` }}
</div>

View File

@ -7,6 +7,7 @@ import { t } from 'fyo';
import { Doc } from 'fyo/model/doc';
import { Action } from 'fyo/model/types';
import { getActions } from 'fyo/utils';
import { ModelNameEnum } from 'models/types';
import { handleErrorWithDialog } from 'src/errorHandling';
import { fyo } from 'src/initFyo';
import router from 'src/router';
@ -288,15 +289,28 @@ function getDeleteAction(doc: Doc): Action {
};
}
async function openEdit(doc: Doc) {
const isFormEdit = [
ModelNameEnum.SalesInvoice,
ModelNameEnum.PurchaseInvoice,
ModelNameEnum.JournalEntry,
].includes(doc.schemaName as ModelNameEnum);
if (isFormEdit) {
return await routeTo(`/edit/${doc.schemaName}/${doc.name!}`);
}
await openQuickEdit({ schemaName: doc.schemaName, name: doc.name! });
}
function getDuplicateAction(doc: Doc): Action {
const isSubmittable = !!doc.schema.isSubmittable;
return {
label: t`Duplicate`,
condition: (doc: Doc) =>
!!(
((isSubmittable && doc && doc.submitted) || !isSubmittable) &&
!doc.notInserted &&
!(doc.cancelled || false)
((isSubmittable && doc.submitted) || !isSubmittable) &&
!doc.notInserted
),
async action() {
await showMessageDialog({
@ -306,7 +320,8 @@ function getDuplicateAction(doc: Doc): Action {
label: t`Yes`,
async action() {
try {
doc.duplicate();
const dupe = await doc.duplicate();
await openEdit(dupe);
return true;
} catch (err) {
handleErrorWithDialog(err as Error, doc);