mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50:56 +00:00
incr: redo duplication (don't sync)
- fix journal entry
This commit is contained in:
parent
fefc79024d
commit
eedb4415ce
@ -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 (
|
||||
|
@ -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) => {
|
||||
delete row.name;
|
||||
delete row.parent;
|
||||
});
|
||||
for (const row of value) {
|
||||
delete row.name;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
@ -113,7 +113,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "writeoff",
|
||||
"label": "Write Off / Refund",
|
||||
"label": "Write Off",
|
||||
"fieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user