Merge remote-tracking branch 'origin/develop' into feat/so-po-advance-payment-status

This commit is contained in:
David Arnold 2023-12-20 12:47:48 +01:00
commit f73685f4f6
No known key found for this signature in database
GPG Key ID: AB15A6AF1101390D
142 changed files with 2182 additions and 742 deletions

View File

@ -28,4 +28,7 @@ b147b85e6ac19a9220cd1e2958a6ebd99373283a
494bd9ef78313436f0424b918f200dab8fc7c20b
# bulk format python code with black
baec607ff5905b1c67531096a9cf50ec7ff00a5d
baec607ff5905b1c67531096a9cf50ec7ff00a5d
# bulk refactor with sourcery
eb9ee3f79b94e594fc6dfa4f6514580e125eee8c

View File

@ -36,7 +36,7 @@ def get_default_cost_center(company):
if not frappe.flags.company_cost_center:
frappe.flags.company_cost_center = {}
if not company in frappe.flags.company_cost_center:
if company not in frappe.flags.company_cost_center:
frappe.flags.company_cost_center[company] = frappe.get_cached_value(
"Company", company, "cost_center"
)
@ -47,7 +47,7 @@ def get_company_currency(company):
"""Returns the default company currency"""
if not frappe.flags.company_currency:
frappe.flags.company_currency = {}
if not company in frappe.flags.company_currency:
if company not in frappe.flags.company_currency:
frappe.flags.company_currency[company] = frappe.db.get_value(
"Company", company, "default_currency", cache=True
)
@ -81,7 +81,7 @@ def is_perpetual_inventory_enabled(company):
if not hasattr(frappe.local, "enable_perpetual_inventory"):
frappe.local.enable_perpetual_inventory = {}
if not company in frappe.local.enable_perpetual_inventory:
if company not in frappe.local.enable_perpetual_inventory:
frappe.local.enable_perpetual_inventory[company] = (
frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0
)
@ -96,7 +96,7 @@ def get_default_finance_book(company=None):
if not hasattr(frappe.local, "default_finance_book"):
frappe.local.default_finance_book = {}
if not company in frappe.local.default_finance_book:
if company not in frappe.local.default_finance_book:
frappe.local.default_finance_book[company] = frappe.get_cached_value(
"Company", company, "default_finance_book"
)
@ -108,7 +108,7 @@ def get_party_account_type(party_type):
if not hasattr(frappe.local, "party_account_types"):
frappe.local.party_account_types = {}
if not party_type in frappe.local.party_account_types:
if party_type not in frappe.local.party_account_types:
frappe.local.party_account_types[party_type] = (
frappe.db.get_value("Party Type", party_type, "account_type") or ""
)

View File

@ -232,7 +232,7 @@ def calculate_monthly_amount(
if amount + already_booked_amount_in_account_currency > item.net_amount:
amount = item.net_amount - already_booked_amount_in_account_currency
if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
if get_first_day(start_date) != start_date or get_last_day(end_date) != end_date:
partial_month = flt(date_diff(end_date, start_date)) / flt(
date_diff(get_last_day(end_date), get_first_day(start_date))
)
@ -358,9 +358,11 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
account_currency = get_account_currency(item.expense_account or item.income_account)
if doc.doctype == "Sales Invoice":
against_type = "Customer"
against, project = doc.customer, doc.project
credit_account, debit_account = item.income_account, item.deferred_revenue_account
else:
against_type = "Supplier"
against, project = doc.supplier, item.project
credit_account, debit_account = item.deferred_expense_account, item.expense_account
@ -413,6 +415,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
doc,
credit_account,
debit_account,
against_type,
against,
amount,
base_amount,
@ -494,6 +497,7 @@ def make_gl_entries(
doc,
credit_account,
debit_account,
against_type,
against,
amount,
base_amount,
@ -515,7 +519,9 @@ def make_gl_entries(
doc.get_gl_dict(
{
"account": credit_account,
"against_type": against_type,
"against": against,
"against_link": against,
"credit": base_amount,
"credit_in_account_currency": amount,
"cost_center": cost_center,
@ -534,7 +540,9 @@ def make_gl_entries(
doc.get_gl_dict(
{
"account": debit_account,
"against_type": against_type,
"against": against,
"against_link": against,
"debit": base_amount,
"debit_in_account_currency": amount,
"cost_center": cost_center,

View File

@ -33,7 +33,9 @@
},
"Stocks": {
"Mati\u00e8res premi\u00e8res": {},
"Stock de produits fini": {},
"Stock de produits fini": {
"account_type": "Stock"
},
"Stock exp\u00e9di\u00e9 non-factur\u00e9": {},
"Travaux en cours": {},
"account_type": "Stock"
@ -395,9 +397,11 @@
},
"Produits": {
"Revenus de ventes": {
" Escomptes de volume sur ventes": {},
"Escomptes de volume sur ventes": {},
"Autres produits d'exploitation": {},
"Ventes": {},
"Ventes": {
"account_type": "Income Account"
},
"Ventes avec des provinces harmonis\u00e9es": {},
"Ventes avec des provinces non-harmonis\u00e9es": {},
"Ventes \u00e0 l'\u00e9tranger": {}

View File

@ -53,8 +53,13 @@
},
"II. Forderungen und sonstige Vermögensgegenstände": {
"is_group": 1,
"Ford. a. Lieferungen und Leistungen": {
"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "1400",
"account_type": "Receivable",
"is_group": 1
},
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1410",
"account_type": "Receivable"
},
"Durchlaufende Posten": {
@ -180,8 +185,13 @@
},
"IV. Verbindlichkeiten aus Lieferungen und Leistungen": {
"is_group": 1,
"Verbindlichkeiten aus Lieferungen u. Leistungen": {
"Verbindlichkeiten aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "1600",
"account_type": "Payable",
"is_group": 1
},
"Verbindlichkeiten aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1610",
"account_type": "Payable"
}
},

View File

@ -407,13 +407,10 @@
"Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": {
"account_number": "9960"
},
"Debitoren": {
"is_group": 1,
"account_number": "10000"
},
"Forderungen aus Lieferungen und Leistungen": {
"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "1200",
"account_type": "Receivable"
"account_type": "Receivable",
"is_group": 1
},
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1210"
@ -1138,18 +1135,15 @@
"Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": {
"account_number": "9964"
},
"Kreditoren": {
"account_number": "70000",
"Verb. aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "3300",
"account_type": "Payable",
"is_group": 1,
"Wareneingangs-­Verrechnungskonto" : {
"Wareneingangs-Verrechnungskonto" : {
"account_number": "70001",
"account_type": "Stock Received But Not Billed"
}
},
"Verb. aus Lieferungen und Leistungen": {
"account_number": "3300",
"account_type": "Payable"
},
"Verb. aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "3310"
},

View File

@ -44,7 +44,7 @@ class ExchangeRateRevaluation(Document):
self.set_total_gain_loss()
def validate_rounding_loss_allowance(self):
if not (self.rounding_loss_allowance >= 0 and self.rounding_loss_allowance < 1):
if self.rounding_loss_allowance < 0 or self.rounding_loss_allowance >= 1:
frappe.throw(_("Rounding Loss Allowance should be between 0 and 1"))
def set_total_gain_loss(self):

View File

@ -17,7 +17,9 @@
"account_currency",
"debit_in_account_currency",
"credit_in_account_currency",
"against_type",
"against",
"against_link",
"against_voucher_type",
"against_voucher",
"voucher_type",
@ -129,12 +131,26 @@
"options": "account_currency"
},
{
"fieldname": "against",
"fieldtype": "Text",
"fieldname": "against_type",
"fieldtype": "Link",
"in_filter": 1,
"label": "Against",
"oldfieldname": "against",
"oldfieldtype": "Text"
"label": "Against Type",
"options": "DocType"
},
{
"fieldname": "against",
"fieldtype": "Text",
"in_filter": 1,
"label": "Against",
"oldfieldname": "against",
"oldfieldtype": "Text"
},
{
"fieldname": "against_link",
"fieldtype": "Dynamic Link",
"in_filter": 1,
"label": "Against",
"options": "against_type"
},
{
"fieldname": "against_voucher_type",
@ -286,7 +302,7 @@
"idx": 1,
"in_create": 1,
"links": [],
"modified": "2023-08-16 21:38:44.072267",
"modified": "2023-11-08 12:20:23.031733",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",

View File

@ -153,7 +153,9 @@ class InvoiceDiscounting(AccountsController):
"account": inv.debit_to,
"party_type": "Customer",
"party": d.customer,
"against_type": "Account",
"against": self.accounts_receivable_credit,
"against_link": self.accounts_receivable_credit,
"credit": outstanding_in_company_currency,
"credit_in_account_currency": outstanding_in_company_currency
if inv.party_account_currency == company_currency
@ -173,7 +175,9 @@ class InvoiceDiscounting(AccountsController):
"account": self.accounts_receivable_credit,
"party_type": "Customer",
"party": d.customer,
"against_type": "Account",
"against": inv.debit_to,
"against_link": inv.debit_to,
"debit": outstanding_in_company_currency,
"debit_in_account_currency": outstanding_in_company_currency
if ar_credit_account_currency == company_currency

View File

@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry");
frappe.ui.form.on("Journal Entry", {
setup: function(frm) {
frm.add_fetch("bank_account", "account", "account");
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', 'Asset Depreciation Schedule', "Repost Accounting Ledger"];
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', 'Asset Depreciation Schedule', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"];
},
refresh: function(frm) {
@ -220,6 +220,16 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
return erpnext.journal_entry.account_query(me.frm);
});
me.frm.set_query("against_account_link", "accounts", function(doc, cdt, cdn) {
return erpnext.journal_entry.against_account_query(me.frm);
});
me.frm.set_query("against_type", "accounts", function(){
return {
query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_type",
}
})
me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
const row = locals[cdt][cdn];
@ -591,6 +601,21 @@ $.extend(erpnext.journal_entry, {
return { filters: filters };
},
against_account_query: function(frm) {
if (frm.doc.against_type != "Account"){
return { filters: {} };
}
else {
let filters = { company: frm.doc.company, is_group: 0 };
if(!frm.doc.multi_currency) {
$.extend(filters, {
account_currency: ['in', [frappe.get_doc(":Company", frm.doc.company).default_currency, null]]
});
}
return { filters: filters };
}
},
reverse_journal_entry: function() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",

View File

@ -170,6 +170,8 @@ class JournalEntry(AccountsController):
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Unreconcile Payment",
"Unreconcile Payment Entries",
)
self.make_gl_entries(1)
self.update_advance_paid()
@ -302,6 +304,7 @@ class JournalEntry(AccountsController):
"account": tax_withholding_details.get("account_head"),
rev_debit_or_credit: tax_withholding_details.get("tax_amount"),
"against_account": parties[0],
"against_account_link": parties[0],
},
)
@ -629,7 +632,7 @@ class JournalEntry(AccountsController):
)
# set totals
if not d.reference_name in self.reference_totals:
if d.reference_name not in self.reference_totals:
self.reference_totals[d.reference_name] = 0.0
if self.voucher_type not in ("Deferred Revenue", "Deferred Expense"):
@ -748,27 +751,90 @@ class JournalEntry(AccountsController):
)
def set_against_account(self):
accounts_debited, accounts_credited = [], []
if self.voucher_type in ("Deferred Revenue", "Deferred Expense"):
for d in self.get("accounts"):
if d.reference_type == "Sales Invoice":
field = "customer"
against_type = "Customer"
else:
field = "supplier"
against_type = "Supplier"
d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
against_account = frappe.db.get_value(d.reference_type, d.reference_name, against_type.lower())
d.against_type = against_type
d.against_account_link = against_account
else:
for d in self.get("accounts"):
if flt(d.debit) > 0:
accounts_debited.append(d.party or d.account)
if flt(d.credit) > 0:
accounts_credited.append(d.party or d.account)
self.get_debited_credited_accounts()
if len(self.accounts_credited) > 1 and len(self.accounts_debited) > 1:
self.auto_set_against_accounts()
return
self.get_against_accounts()
for d in self.get("accounts"):
if flt(d.debit) > 0:
d.against_account = ", ".join(list(set(accounts_credited)))
if flt(d.credit) > 0:
d.against_account = ", ".join(list(set(accounts_debited)))
def auto_set_against_accounts(self):
for i in range(0, len(self.accounts), 2):
acc = self.accounts[i]
against_acc = self.accounts[i + 1]
if acc.debit_in_account_currency > 0:
current_val = acc.debit_in_account_currency * flt(acc.exchange_rate)
against_val = against_acc.credit_in_account_currency * flt(against_acc.exchange_rate)
else:
current_val = acc.credit_in_account_currency * flt(acc.exchange_rate)
against_val = against_acc.debit_in_account_currency * flt(against_acc.exchange_rate)
if current_val == against_val:
acc.against_type = against_acc.party_type or "Account"
against_acc.against_type = acc.party_type or "Account"
acc.against_account_link = against_acc.party or against_acc.account
against_acc.against_account_link = acc.party or acc.account
else:
frappe.msgprint(
_(
"Unable to automatically determine {0} accounts. Set them up in the {1} table if needed."
).format(frappe.bold("against"), frappe.bold("Accounting Entries")),
alert=True,
)
break
def get_against_accounts(self):
self.against_accounts = []
self.split_account = {}
self.get_debited_credited_accounts()
if self.separate_against_account_entries:
no_of_credited_acc, no_of_debited_acc = len(self.accounts_credited), len(self.accounts_debited)
if no_of_credited_acc <= 1 and no_of_debited_acc <= 1:
self.set_against_accounts_for_single_dr_cr()
self.separate_against_account_entries = 0
elif no_of_credited_acc == 1:
self.against_accounts = self.accounts_debited
self.split_account = self.accounts_credited[0]
elif no_of_debited_acc == 1:
self.against_accounts = self.accounts_credited
self.split_account = self.accounts_debited[0]
def get_debited_credited_accounts(self):
self.accounts_debited, self.accounts_credited = [], []
self.separate_against_account_entries = 1
for d in self.get("accounts"):
if flt(d.debit) > 0:
self.accounts_debited.append(d)
elif flt(d.credit) > 0:
self.accounts_credited.append(d)
if d.against_account_link:
self.separate_against_account_entries = 0
break
def set_against_accounts_for_single_dr_cr(self):
against_account = None
for d in self.accounts:
if flt(d.debit) > 0:
against_account = self.accounts_credited[0]
elif flt(d.credit) > 0:
against_account = self.accounts_debited[0]
if against_account:
d.against_type = against_account.party_type or "Account"
d.against_account = against_account.party or against_account.account
d.against_account_link = against_account.party or against_account.account
def validate_debit_credit_amount(self):
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
@ -965,40 +1031,82 @@ class JournalEntry(AccountsController):
def build_gl_map(self):
gl_map = []
self.get_against_accounts()
for d in self.get("accounts"):
if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
r = [d.user_remark, self.remark]
r = [x for x in r if x]
remarks = "\n".join(r)
gl_map.append(
self.get_gl_dict(
{
"account": d.account,
"party_type": d.party_type,
"due_date": self.due_date,
"party": d.party,
"against": d.against_account,
"debit": flt(d.debit, d.precision("debit")),
"credit": flt(d.credit, d.precision("credit")),
"account_currency": d.account_currency,
"debit_in_account_currency": flt(
d.debit_in_account_currency, d.precision("debit_in_account_currency")
),
"credit_in_account_currency": flt(
d.credit_in_account_currency, d.precision("credit_in_account_currency")
),
"against_voucher_type": d.reference_type,
"against_voucher": d.reference_name,
"remarks": remarks,
"voucher_detail_no": d.reference_detail_no,
"cost_center": d.cost_center,
"project": d.project,
"finance_book": self.finance_book,
},
item=d,
)
gl_dict = self.get_gl_dict(
{
"account": d.account,
"party_type": d.party_type,
"due_date": self.due_date,
"party": d.party,
"debit": flt(d.debit, d.precision("debit")),
"credit": flt(d.credit, d.precision("credit")),
"account_currency": d.account_currency,
"debit_in_account_currency": flt(
d.debit_in_account_currency, d.precision("debit_in_account_currency")
),
"credit_in_account_currency": flt(
d.credit_in_account_currency, d.precision("credit_in_account_currency")
),
"against_voucher_type": d.reference_type,
"against_voucher": d.reference_name,
"remarks": remarks,
"voucher_detail_no": d.reference_detail_no,
"cost_center": d.cost_center,
"project": d.project,
"finance_book": self.finance_book,
},
item=d,
)
if not self.separate_against_account_entries:
gl_dict.update(
{
"against_type": d.against_type,
"against_link": d.against_account_link,
}
)
gl_map.append(gl_dict)
elif d in self.against_accounts:
gl_dict.update(
{
"against_type": self.split_account.get("party_type") or "Account",
"against": self.split_account.get("party") or self.split_account.get("account"),
"against_link": self.split_account.get("party") or self.split_account.get("account"),
}
)
gl_map.append(gl_dict)
else:
for against_account in self.against_accounts:
against_account = against_account.as_dict()
debit = against_account.credit or against_account.credit_in_account_currency
credit = against_account.debit or against_account.debit_in_account_currency
gl_dict = gl_dict.copy()
gl_dict.update(
{
"against_type": against_account.party_type or "Account",
"against": against_account.party or against_account.account,
"against_link": against_account.party or against_account.account,
"debit": flt(debit, d.precision("debit")),
"credit": flt(credit, d.precision("credit")),
"account_currency": d.account_currency,
"debit_in_account_currency": flt(
debit / d.exchange_rate, d.precision("debit_in_account_currency")
),
"credit_in_account_currency": flt(
credit / d.exchange_rate, d.precision("credit_in_account_currency")
),
}
)
gl_map.append(gl_dict)
return gl_map
def make_gl_entries(self, cancel=0, adv_adj=0):
@ -1623,3 +1731,10 @@ def make_reverse_journal_entry(source_name, target_doc=None):
)
return doclist
@frappe.whitelist()
def get_against_type(doctype, txt, searchfield, start, page_len, filters):
against_types = frappe.db.get_list("Party Type", pluck="name") + ["Account"]
doctype = frappe.qb.DocType("DocType")
return frappe.qb.from_(doctype).select(doctype.name).where(doctype.name.isin(against_types)).run()

View File

@ -37,7 +37,9 @@
"col_break3",
"is_advance",
"user_remark",
"against_account"
"against_type",
"against_account",
"against_account_link"
],
"fields": [
{
@ -250,14 +252,21 @@
"print_hide": 1
},
{
"fieldname": "against_account",
"fieldtype": "Text",
"hidden": 1,
"fieldname": "against_account",
"fieldtype": "Text",
"hidden": 1,
"label": "Against Account",
"no_copy": 1,
"oldfieldname": "against_account",
"oldfieldtype": "Text",
"print_hide": 1
},
{
"fieldname": "against_account_link",
"fieldtype": "Dynamic Link",
"label": "Against Account",
"no_copy": 1,
"oldfieldname": "against_account",
"oldfieldtype": "Text",
"print_hide": 1
"options": "against_type"
},
{
"collapsible": 1,
@ -280,14 +289,19 @@
"fieldtype": "Data",
"hidden": 1,
"label": "Reference Detail No",
"no_copy": 1,
"search_index": 1
"no_copy": 1
},
{
"fieldname": "against_type",
"fieldtype": "Link",
"label": "Against Type",
"options": "DocType"
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-11-23 11:44:25.841187",
"modified": "2023-12-02 23:21:22.205409",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

View File

@ -140,7 +140,7 @@ class TestLoyaltyProgram(unittest.TestCase):
"Loyalty Point Entry",
{"invoice_type": "Sales Invoice", "invoice": si.name, "customer": si.customer},
)
self.assertEqual(True, not (lpe is None))
self.assertEqual(True, lpe is not None)
# cancelling sales invoice
si.cancel()

View File

@ -369,12 +369,12 @@ class PaymentEntry(AccountsController):
self.set(self.party_account_field, party_account)
self.party_account = party_account
if self.paid_from and not (self.paid_from_account_currency or self.paid_from_account_balance):
if self.paid_from and not self.paid_from_account_currency and not self.paid_from_account_balance:
acc = get_account_details(self.paid_from, self.posting_date, self.cost_center)
self.paid_from_account_currency = acc.account_currency
self.paid_from_account_balance = acc.account_balance
if self.paid_to and not (self.paid_to_account_currency or self.paid_to_account_balance):
if self.paid_to and not self.paid_to_account_currency and not self.paid_to_account_balance:
acc = get_account_details(self.paid_to, self.posting_date, self.cost_center)
self.paid_to_account_currency = acc.account_currency
self.paid_to_account_balance = acc.account_balance
@ -390,8 +390,9 @@ class PaymentEntry(AccountsController):
) -> None:
for d in self.get("references"):
if d.allocated_amount:
if update_ref_details_only_for and (
not (d.reference_doctype, d.reference_name) in update_ref_details_only_for
if (
update_ref_details_only_for
and (d.reference_doctype, d.reference_name) not in update_ref_details_only_for
):
continue
@ -702,7 +703,7 @@ class PaymentEntry(AccountsController):
self.db_set("status", self.status, update_modified=True)
def set_tax_withholding(self):
if not self.party_type == "Supplier":
if self.party_type != "Supplier":
return
if not self.apply_tax_withholding_amount:
@ -793,7 +794,7 @@ class PaymentEntry(AccountsController):
self.base_received_amount = self.base_paid_amount
if (
self.paid_from_account_currency == self.paid_to_account_currency
and not self.payment_type == "Internal Transfer"
and self.payment_type != "Internal Transfer"
):
self.received_amount = self.paid_amount
@ -1060,7 +1061,9 @@ class PaymentEntry(AccountsController):
"account": self.party_account,
"party_type": self.party_type,
"party": self.party,
"against_type": "Account",
"against": against_account,
"against_link": against_account,
"account_currency": self.party_account_currency,
"cost_center": self.cost_center,
},
@ -1225,7 +1228,9 @@ class PaymentEntry(AccountsController):
{
"account": self.paid_from,
"account_currency": self.paid_from_account_currency,
"against_type": self.party_type if self.payment_type == "Pay" else "Account",
"against": self.party if self.payment_type == "Pay" else self.paid_to,
"against_link": self.party if self.payment_type == "Pay" else self.paid_to,
"credit_in_account_currency": self.paid_amount,
"credit": self.base_paid_amount,
"cost_center": self.cost_center,
@ -1240,7 +1245,9 @@ class PaymentEntry(AccountsController):
{
"account": self.paid_to,
"account_currency": self.paid_to_account_currency,
"against_type": self.party_type if self.payment_type == "Receive" else "Account",
"against": self.party if self.payment_type == "Receive" else self.paid_from,
"against_link": self.party if self.payment_type == "Receive" else self.paid_from,
"debit_in_account_currency": self.received_amount,
"debit": self.base_received_amount,
"cost_center": self.cost_center,
@ -1264,6 +1271,7 @@ class PaymentEntry(AccountsController):
rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
against = self.party or self.paid_to
against_type = self.party_type or "Account"
payment_account = self.get_party_account_for_taxes()
tax_amount = d.tax_amount
base_tax_amount = d.base_tax_amount
@ -1272,7 +1280,9 @@ class PaymentEntry(AccountsController):
self.get_gl_dict(
{
"account": d.account_head,
"against_type": against_type,
"against": against,
"against_link": against,
dr_or_cr: tax_amount,
dr_or_cr + "_in_account_currency": base_tax_amount
if account_currency == self.company_currency
@ -1297,7 +1307,9 @@ class PaymentEntry(AccountsController):
self.get_gl_dict(
{
"account": payment_account,
"against_type": against_type,
"against": against,
"against_link": against,
rev_dr_or_cr: tax_amount,
rev_dr_or_cr + "_in_account_currency": base_tax_amount
if account_currency == self.company_currency
@ -1322,7 +1334,9 @@ class PaymentEntry(AccountsController):
{
"account": d.account,
"account_currency": account_currency,
"against_type": self.party_type or "Account",
"against": self.party or self.paid_from,
"against_link": self.party or self.paid_from,
"debit_in_account_currency": d.amount,
"debit": d.amount,
"cost_center": d.cost_center,
@ -1757,7 +1771,7 @@ def get_split_invoice_rows(invoice: dict, payment_term_template: str, exc_rates:
"Payment Schedule", filters={"parent": invoice.voucher_no}, fields=["*"], order_by="due_date"
)
for payment_term in payment_schedule:
if not payment_term.outstanding > 0.1:
if payment_term.outstanding <= 0.1:
continue
doc_details = exc_rates.get(payment_term.parent, None)

View File

@ -594,6 +594,27 @@ class PaymentReconciliation(Document):
invoice_exchange_map.update(purchase_invoice_map)
journals = [
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Journal Entry"
]
journals.extend(
[d.get("reference_name") for d in payments if d.get("reference_type") == "Journal Entry"]
)
if journals:
journals = list(set(journals))
journals_map = frappe._dict(
frappe.db.get_all(
"Journal Entry Account",
filters={"parent": ("in", journals), "account": ("in", [self.receivable_payable_account])},
fields=[
"parent as `name`",
"exchange_rate",
],
as_list=1,
)
)
invoice_exchange_map.update(journals_map)
return invoice_exchange_map
def validate_allocation(self):

View File

@ -34,4 +34,6 @@ class PaymentReconciliationAllocation(Document):
unreconciled_amount: DF.Currency
# end: auto-generated types
pass
@staticmethod
def get_list(args):
pass

View File

@ -26,4 +26,6 @@ class PaymentReconciliationInvoice(Document):
parenttype: DF.Data
# end: auto-generated types
pass
@staticmethod
def get_list(args):
pass

View File

@ -30,4 +30,6 @@ class PaymentReconciliationPayment(Document):
remark: DF.SmallText | None
# end: auto-generated types
pass
@staticmethod
def get_list(args):
pass

View File

@ -286,7 +286,7 @@ class PricingRule(Document):
def validate_price_list_with_currency(self):
if self.currency and self.for_price_list:
price_list_currency = frappe.db.get_value("Price List", self.for_price_list, "currency", True)
if not self.currency == price_list_currency:
if self.currency != price_list_currency:
throw(_("Currency should be same as Price List Currency: {0}").format(price_list_currency))
def validate_dates(self):

View File

@ -581,6 +581,8 @@ def apply_pricing_rule_on_transaction(doc):
if d.price_or_product_discount == "Price":
if d.apply_discount_on:
doc.set("apply_discount_on", d.apply_discount_on)
# Variable to track whether the condition has been met
condition_met = False
for field in ["additional_discount_percentage", "discount_amount"]:
pr_field = "discount_percentage" if field == "additional_discount_percentage" else field
@ -603,6 +605,11 @@ def apply_pricing_rule_on_transaction(doc):
if coupon_code_pricing_rule == d.name:
# if selected coupon code is linked with pricing rule
doc.set(field, d.get(pr_field))
# Set the condition_met variable to True and break out of the loop
condition_met = True
break
else:
# reset discount if not linked
doc.set(field, 0)
@ -611,6 +618,10 @@ def apply_pricing_rule_on_transaction(doc):
doc.set(field, 0)
doc.calculate_taxes_and_totals()
# Break out of the main loop if the condition is met
if condition_met:
break
elif d.price_or_product_discount == "Product":
item_details = frappe._dict({"parenttype": doc.doctype, "free_item_data": []})
get_product_discount_rule(d, item_details, doc=doc)

View File

@ -475,7 +475,7 @@ def reconcile(doc: None | str = None) -> None:
frappe.db.set_value("Process Payment Reconciliation", doc, "status", "Completed")
else:
if not (frappe.db.get_value("Process Payment Reconciliation", doc, "status") == "Paused"):
if frappe.db.get_value("Process Payment Reconciliation", doc, "status") != "Paused":
# trigger next batch in job
# generate reconcile job name
allocation = get_next_allocation(log)

View File

@ -15,6 +15,7 @@
"group_by",
"cost_center",
"territory",
"ignore_exchange_rate_revaluation_journals",
"column_break_14",
"to_date",
"finance_book",
@ -376,10 +377,16 @@
"fieldname": "pdf_name",
"fieldtype": "Data",
"label": "PDF Name"
},
{
"default": "0",
"fieldname": "ignore_exchange_rate_revaluation_journals",
"fieldtype": "Check",
"label": "Ignore Exchange Rate Revaluation Journals"
}
],
"links": [],
"modified": "2023-08-28 12:59:53.071334",
"modified": "2023-12-18 12:20:08.965120",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts",

View File

@ -56,6 +56,7 @@ class ProcessStatementOfAccounts(Document):
frequency: DF.Literal["Weekly", "Monthly", "Quarterly"]
from_date: DF.Date | None
group_by: DF.Literal["", "Group by Voucher", "Group by Voucher (Consolidated)"]
ignore_exchange_rate_revaluation_journals: DF.Check
include_ageing: DF.Check
include_break: DF.Check
letter_head: DF.Link | None
@ -119,6 +120,18 @@ def get_statement_dict(doc, get_statement_dict=False):
statement_dict = {}
ageing = ""
err_journals = None
if doc.report == "General Ledger" and doc.ignore_exchange_rate_revaluation_journals:
err_journals = frappe.db.get_all(
"Journal Entry",
filters={
"company": doc.company,
"docstatus": 1,
"voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]),
},
as_list=True,
)
for entry in doc.customers:
if doc.include_ageing:
ageing = set_ageing(doc, entry)
@ -131,6 +144,8 @@ def get_statement_dict(doc, get_statement_dict=False):
)
filters = get_common_filters(doc)
if err_journals:
filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
if doc.report == "General Ledger":
filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency))

View File

@ -35,7 +35,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
super.onload();
// Ignore linked advances
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger"];
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"];
if(!this.frm.doc.__islocal) {
// show credit_to in print format

View File

@ -371,7 +371,7 @@ class PurchaseInvoice(BuyingController):
check_list = []
for d in self.get("items"):
if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt:
if d.purchase_order and d.purchase_order not in check_list and not d.purchase_receipt:
check_list.append(d.purchase_order)
check_on_hold_or_closed_status("Purchase Order", d.purchase_order)
@ -815,7 +815,9 @@ class PurchaseInvoice(BuyingController):
"party_type": "Supplier",
"party": self.supplier,
"due_date": self.due_date,
"against_type": "Account",
"against": self.against_expense_account,
"against_link": self.against_expense_account,
"credit": base_grand_total,
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
@ -888,7 +890,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": warehouse_account[item.warehouse]["account"],
"against_type": "Account",
"against": warehouse_account[item.from_warehouse]["account"],
"against_link": warehouse_account[item.from_warehouse]["account"],
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@ -908,7 +912,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": warehouse_account[item.from_warehouse]["account"],
"against_type": "Account",
"against": warehouse_account[item.warehouse]["account"],
"against_link": warehouse_account[item.warehouse]["account"],
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@ -925,7 +931,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": item.expense_account,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
@ -942,7 +950,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": item.expense_account,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"debit": warehouse_debit_amount,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
@ -961,7 +971,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": account,
"against_type": "Account",
"against": item.expense_account,
"against_link": item.expense_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(amount["base_amount"]),
@ -981,7 +993,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": supplier_warehouse_account,
"against_type": "Account",
"against": item.expense_account,
"against_link": item.expense_account,
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@ -1036,7 +1050,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": expense_account,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"debit": amount,
"cost_center": item.cost_center,
"project": item.project or self.project,
@ -1062,7 +1078,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": expense_account,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"debit": discrepancy_caused_by_exchange_rate_difference,
"cost_center": item.cost_center,
"project": item.project or self.project,
@ -1075,7 +1093,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": self.get_company_default("exchange_gain_loss_account"),
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"credit": discrepancy_caused_by_exchange_rate_difference,
"cost_center": item.cost_center,
"project": item.project or self.project,
@ -1120,7 +1140,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": stock_rbnb,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
"remarks": self.remarks or _("Accounting Entry for Stock"),
"cost_center": self.cost_center,
@ -1168,7 +1190,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": cost_of_goods_sold_account,
"against_type": "Account",
"against": item.expense_account,
"against_link": item.expense_account,
"debit": stock_adjustment_amt,
"remarks": self.get("remarks") or _("Stock Adjustment"),
"cost_center": item.cost_center,
@ -1198,7 +1222,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": tax.account_head,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
dr_or_cr: base_amount,
dr_or_cr + "_in_account_currency": base_amount
if account_currency == self.company_currency
@ -1246,8 +1272,10 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": tax.account_head,
"against_type": "Supplier",
"cost_center": tax.cost_center,
"against": self.supplier,
"against_link": self.supplier,
"credit": applicable_amount,
"remarks": self.remarks or _("Accounting Entry for Stock"),
},
@ -1265,7 +1293,9 @@ class PurchaseInvoice(BuyingController):
{
"account": tax.account_head,
"cost_center": tax.cost_center,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"credit": valuation_tax[tax.name],
"remarks": self.remarks or _("Accounting Entry for Stock"),
},
@ -1280,7 +1310,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": self.unrealized_profit_loss_account,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"credit": flt(self.total_taxes_and_charges),
"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
"cost_center": self.cost_center,
@ -1301,7 +1333,9 @@ class PurchaseInvoice(BuyingController):
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"against_type": "Account",
"against": self.cash_bank_account,
"against_link": self.cash_bank_account,
"debit": self.base_paid_amount,
"debit_in_account_currency": self.base_paid_amount
if self.party_account_currency == self.company_currency
@ -1322,7 +1356,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": self.cash_bank_account,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"credit": self.base_paid_amount,
"credit_in_account_currency": self.base_paid_amount
if bank_account_currency == self.company_currency
@ -1346,7 +1382,9 @@ class PurchaseInvoice(BuyingController):
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"against_type": "Account",
"against": self.write_off_account,
"against_link": self.write_off_account,
"debit": self.base_write_off_amount,
"debit_in_account_currency": self.base_write_off_amount
if self.party_account_currency == self.company_currency
@ -1366,7 +1404,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": self.write_off_account,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"credit": flt(self.base_write_off_amount),
"credit_in_account_currency": self.base_write_off_amount
if write_off_account_currency == self.company_currency
@ -1393,7 +1433,9 @@ class PurchaseInvoice(BuyingController):
self.get_gl_dict(
{
"account": round_off_account,
"against_type": "Supplier",
"against": self.supplier,
"against_link": self.supplier,
"debit_in_account_currency": self.rounding_adjustment,
"debit": self.base_rounding_adjustment,
"cost_center": round_off_cost_center
@ -1449,6 +1491,8 @@ class PurchaseInvoice(BuyingController):
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Unreconcile Payment",
"Unreconcile Payment Entries",
"Payment Ledger Entry",
"Tax Withheld Vouchers",
"Serial and Batch Bundle",

View File

@ -126,7 +126,7 @@ class RepostAccountingLedger(Document):
return rendered_page
def on_submit(self):
if len(self.vouchers) > 1:
if len(self.vouchers) > 5:
job_name = "repost_accounting_ledger_" + self.name
frappe.enqueue(
method="erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.start_repost",
@ -170,8 +170,6 @@ def start_repost(account_repost_doc=str) -> None:
doc.make_gl_entries(1)
doc.make_gl_entries()
frappe.db.commit()
def get_allowed_types_from_settings():
return [

View File

@ -20,18 +20,11 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
self.create_company()
self.create_customer()
self.create_item()
self.update_repost_settings()
update_repost_settings()
def teadDown(self):
def tearDown(self):
frappe.db.rollback()
def update_repost_settings(self):
allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]
repost_settings = frappe.get_doc("Repost Accounting Ledger Settings")
for x in allowed_types:
repost_settings.append("allowed_types", {"document_type": x, "allowed": True})
repost_settings.save()
def test_01_basic_functions(self):
si = create_sales_invoice(
item=self.item,
@ -90,9 +83,6 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
# Submit repost document
ral.save().submit()
# background jobs don't run on test cases. Manually triggering repost function.
start_repost(ral.name)
res = (
qb.from_(gl)
.select(gl.voucher_no, Sum(gl.debit).as_("debit"), Sum(gl.credit).as_("credit"))
@ -177,26 +167,6 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
pe = get_payment_entry(si.doctype, si.name)
pe.save().submit()
# without deletion flag set
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
ral.delete_cancelled_entries = False
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
ral.save()
# assert preview data is generated
preview = ral.generate_preview()
self.assertIsNotNone(preview)
ral.save().submit()
# background jobs don't run on test cases. Manually triggering repost function.
start_repost(ral.name)
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
# with deletion flag set
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
@ -205,6 +175,38 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
ral.save().submit()
start_repost(ral.name)
self.assertIsNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
def test_05_without_deletion_flag(self):
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=100,
)
pe = get_payment_entry(si.doctype, si.name)
pe.save().submit()
# without deletion flag set
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
ral.delete_cancelled_entries = False
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
ral.save().submit()
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
def update_repost_settings():
allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]
repost_settings = frappe.get_doc("Repost Accounting Ledger Settings")
for x in allowed_types:
repost_settings.append("allowed_types", {"document_type": x, "allowed": True})
repost_settings.save()

View File

@ -7,7 +7,7 @@ from frappe import _, msgprint, throw
from frappe.contacts.doctype.address.address import get_address_display
from frappe.model.mapper import get_mapped_doc
from frappe.model.utils import get_fetch_values
from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
from frappe.utils import add_days, cint, flt, formatdate, get_link_to_form, getdate, nowdate
import erpnext
from erpnext.accounts.deferred_revenue import validate_service_stop_date
@ -458,7 +458,7 @@ class SalesInvoice(SellingController):
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.check_credit_limit()
if not cint(self.is_pos) == 1 and not self.is_return:
if cint(self.is_pos) != 1 and not self.is_return:
self.update_against_document_in_jv()
self.update_time_sheet(self.name)
@ -1225,7 +1225,9 @@ class SalesInvoice(SellingController):
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against_type": "Account",
"against": self.against_income_account,
"against_link": self.against_income_account,
"debit": base_grand_total,
"debit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
@ -1254,7 +1256,9 @@ class SalesInvoice(SellingController):
self.get_gl_dict(
{
"account": tax.account_head,
"against_type": "Customer",
"against": self.customer,
"against_link": self.customer,
"credit": flt(base_amount, tax.precision("tax_amount_after_discount_amount")),
"credit_in_account_currency": (
flt(base_amount, tax.precision("base_tax_amount_after_discount_amount"))
@ -1275,7 +1279,9 @@ class SalesInvoice(SellingController):
self.get_gl_dict(
{
"account": self.unrealized_profit_loss_account,
"against_type": "Customer",
"against": self.customer,
"against_link": self.customer,
"debit": flt(self.total_taxes_and_charges),
"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
"cost_center": self.cost_center,
@ -1343,7 +1349,9 @@ class SalesInvoice(SellingController):
add_asset_activity(asset.name, _("Asset sold"))
for gle in fixed_asset_gl_entries:
gle["against_type"] = "Customer"
gle["against"] = self.customer
gle["against_link"] = self.customer
gl_entries.append(self.get_gl_dict(gle, item=item))
self.set_asset_status(asset)
@ -1364,7 +1372,9 @@ class SalesInvoice(SellingController):
self.get_gl_dict(
{
"account": income_account,
"against_type": "Customer",
"against": self.customer,
"against_link": self.customer,
"credit": flt(base_amount, item.precision("base_net_amount")),
"credit_in_account_currency": (
flt(base_amount, item.precision("base_net_amount"))
@ -1418,9 +1428,9 @@ class SalesInvoice(SellingController):
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
"against": "Expense account - "
+ cstr(self.loyalty_redemption_account)
+ " for the Loyalty Program",
"against_type": "Account",
"against": self.loyalty_redemption_account,
"against_link": self.loyalty_redemption_account,
"credit": self.loyalty_amount,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
@ -1434,7 +1444,9 @@ class SalesInvoice(SellingController):
{
"account": self.loyalty_redemption_account,
"cost_center": self.cost_center or self.loyalty_redemption_cost_center,
"against_type": "Customer",
"against": self.customer,
"against_link": self.customer,
"debit": self.loyalty_amount,
"remark": "Loyalty Points redeemed by the customer",
},
@ -1461,7 +1473,9 @@ class SalesInvoice(SellingController):
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
"against_type": "Account",
"against": payment_mode.account,
"against_link": payment_mode.account,
"credit": payment_mode.base_amount,
"credit_in_account_currency": payment_mode.base_amount
if self.party_account_currency == self.company_currency
@ -1482,7 +1496,9 @@ class SalesInvoice(SellingController):
self.get_gl_dict(
{
"account": payment_mode.account,
"against_type": "Customer",
"against": self.customer,
"against_link": self.customer,
"debit": payment_mode.base_amount,
"debit_in_account_currency": payment_mode.base_amount
if payment_mode_account_currency == self.company_currency
@ -1506,7 +1522,9 @@ class SalesInvoice(SellingController):
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
"against_type": "Account",
"against": self.account_for_change_amount,
"against_link": self.account_for_change_amount,
"debit": flt(self.base_change_amount),
"debit_in_account_currency": flt(self.base_change_amount)
if self.party_account_currency == self.company_currency
@ -1527,7 +1545,9 @@ class SalesInvoice(SellingController):
self.get_gl_dict(
{
"account": self.account_for_change_amount,
"against_type": "Customer",
"against": self.customer,
"against_link": self.customer,
"credit": self.base_change_amount,
"cost_center": self.cost_center,
},
@ -1553,7 +1573,9 @@ class SalesInvoice(SellingController):
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
"against_type": "Account",
"against": self.write_off_account,
"against_link": self.write_off_account,
"credit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
"credit_in_account_currency": (
flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
@ -1573,7 +1595,9 @@ class SalesInvoice(SellingController):
self.get_gl_dict(
{
"account": self.write_off_account,
"against_type": "Customer",
"against": self.customer,
"against_link": self.customer,
"debit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
"debit_in_account_currency": (
flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
@ -1601,7 +1625,9 @@ class SalesInvoice(SellingController):
self.get_gl_dict(
{
"account": round_off_account,
"against_type": "Customer",
"against": self.customer,
"against_link": self.customer,
"credit_in_account_currency": flt(
self.rounding_adjustment, self.precision("rounding_adjustment")
),
@ -1960,9 +1986,9 @@ def validate_inter_company_party(doctype, party, company, inter_company_referenc
if inter_company_reference:
doc = frappe.get_doc(ref_doc, inter_company_reference)
ref_party = doc.supplier if doctype in ["Sales Invoice", "Sales Order"] else doc.customer
if not frappe.db.get_value(partytype, {"represents_company": doc.company}, "name") == party:
if frappe.db.get_value(partytype, {"represents_company": doc.company}, "name") != party:
frappe.throw(_("Invalid {0} for Inter Company Transaction.").format(_(partytype)))
if not frappe.get_cached_value(ref_partytype, ref_party, "represents_company") == company:
if frappe.get_cached_value(ref_partytype, ref_party, "represents_company") != company:
frappe.throw(_("Invalid Company for Inter Company Transaction."))
elif frappe.db.get_value(partytype, {"name": party, internal: 1}, "name") == party:
@ -1972,7 +1998,7 @@ def validate_inter_company_party(doctype, party, company, inter_company_referenc
filters={"parenttype": partytype, "parent": party},
)
companies = [d.company for d in companies]
if not company in companies:
if company not in companies:
frappe.throw(
_("{0} not allowed to transact with {1}. Please change the Company.").format(
_(partytype), company
@ -2356,9 +2382,18 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
def get_received_items(reference_name, doctype, reference_fieldname):
reference_field = "inter_company_invoice_reference"
if doctype == "Purchase Order":
reference_field = "inter_company_order_reference"
filters = {
reference_field: reference_name,
"docstatus": 1,
}
target_doctypes = frappe.get_all(
doctype,
filters={"inter_company_invoice_reference": reference_name, "docstatus": 1},
filters=filters,
as_list=True,
)

View File

@ -2793,6 +2793,12 @@ class TestSalesInvoice(FrappeTestCase):
@change_settings("Selling Settings", {"enable_discount_accounting": 1})
def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled(self):
from erpnext.accounts.doctype.repost_accounting_ledger.test_repost_accounting_ledger import (
update_repost_settings,
)
update_repost_settings()
additional_discount_account = create_account(
account_name="Discount Account",
parent_account="Indirect Expenses - _TC",

View File

@ -142,12 +142,12 @@ class ShippingRule(Document):
}
if self.shipping_rule_type == "Selling":
# check if not applied on purchase
if not doc.meta.get_field("taxes").options == "Sales Taxes and Charges":
if doc.meta.get_field("taxes").options != "Sales Taxes and Charges":
frappe.throw(_("Shipping rule only applicable for Selling"))
shipping_charge["doctype"] = "Sales Taxes and Charges"
else:
# check if not applied on sales
if not doc.meta.get_field("taxes").options == "Purchase Taxes and Charges":
if doc.meta.get_field("taxes").options != "Purchase Taxes and Charges":
frappe.throw(_("Shipping rule only applicable for Buying"))
shipping_charge["doctype"] = "Purchase Taxes and Charges"

View File

@ -676,7 +676,7 @@ class Subscription(Document):
to_generate_invoice = (
True
if self.status == "Active"
and not self.generate_invoice_at == "Beginning of the current subscription period"
and self.generate_invoice_at != "Beginning of the current subscription period"
else False
)
self.status = "Cancelled"
@ -694,7 +694,7 @@ class Subscription(Document):
subscription and the `Subscription` will lose all the history of generated invoices
it has.
"""
if not self.status == "Cancelled":
if self.status != "Cancelled":
frappe.throw(_("You cannot restart a Subscription that is not cancelled."), InvoiceNotCancelled)
self.status = "Active"

View File

@ -37,7 +37,7 @@ class UnreconcilePayment(Document):
def validate(self):
self.supported_types = ["Payment Entry", "Journal Entry"]
if not self.voucher_type in self.supported_types:
if self.voucher_type not in self.supported_types:
frappe.throw(_("Only {0} are supported").format(comma_and(self.supported_types)))
@frappe.whitelist()

View File

@ -280,6 +280,7 @@ def check_if_in_list(gle, gl_map, dimensions=None):
"project",
"finance_book",
"voucher_no",
"against_link",
]
if dimensions:

View File

@ -775,7 +775,7 @@ def validate_party_frozen_disabled(party_type, party_name):
frozen_accounts_modifier = frappe.db.get_single_value(
"Accounts Settings", "frozen_accounts_modifier"
)
if not frozen_accounts_modifier in frappe.get_roles():
if frozen_accounts_modifier not in frappe.get_roles():
frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
elif party_type == "Employee":

View File

@ -123,7 +123,7 @@ class ReceivablePayableReport(object):
else:
key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
if not key in self.voucher_balance:
if key not in self.voucher_balance:
self.voucher_balance[key] = frappe._dict(
voucher_type=ple.voucher_type,
voucher_no=ple.voucher_no,
@ -225,7 +225,7 @@ class ReceivablePayableReport(object):
if not row:
return
if self.filters.get("in_party_currency"):
if self.filters.get("in_party_currency") or self.filters.get("party_account"):
amount = ple.amount_in_account_currency
else:
amount = ple.amount
@ -244,8 +244,12 @@ class ReceivablePayableReport(object):
row.invoiced_in_account_currency += amount_in_account_currency
else:
if self.is_invoice(ple):
row.credit_note -= amount
row.credit_note_in_account_currency -= amount_in_account_currency
if row.voucher_no == ple.voucher_no == ple.against_voucher_no:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
else:
row.credit_note -= amount
row.credit_note_in_account_currency -= amount_in_account_currency
else:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
@ -451,7 +455,7 @@ class ReceivablePayableReport(object):
party_details = self.get_party_details(row.party) or {}
row.update(party_details)
if self.filters.get("in_party_currency"):
if self.filters.get("in_party_currency") or self.filters.get("party_account"):
row.currency = row.account_currency
else:
row.currency = self.company_currency
@ -938,7 +942,7 @@ class ReceivablePayableReport(object):
return True
def get_party_details(self, party):
if not party in self.party_details:
if party not in self.party_details:
if self.account_type == "Receivable":
fields = ["customer_name", "territory", "customer_group", "customer_primary_contact"]

View File

@ -76,6 +76,41 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
return credit_note
def test_pos_receivable(self):
filters = {
"company": self.company,
"party_type": "Customer",
"party": [self.customer],
"report_date": add_days(today(), 2),
"based_on_payment_terms": 0,
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
"show_remarks": False,
}
pos_inv = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
pos_inv.posting_date = add_days(today(), 2)
pos_inv.is_pos = 1
pos_inv.append(
"payments",
frappe._dict(
mode_of_payment="Cash",
amount=flt(pos_inv.grand_total / 2),
),
)
pos_inv.disable_rounded_total = 1
pos_inv.save()
pos_inv.submit()
report = execute(filters)
expected_data = [[pos_inv.grand_total, pos_inv.paid_amount, 0]]
row = report[1][-1]
self.assertEqual(expected_data[0], [row.invoiced, row.paid, row.credit_note])
pos_inv.cancel()
def test_accounts_receivable(self):
filters = {
"company": self.company,
@ -544,7 +579,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
filters.update({"party_account": self.debtors_usd})
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [8000.0, 8000.0, self.debtors_usd, si2.currency]
expected_data = [100.0, 100.0, self.debtors_usd, si2.currency]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.party_account, row.account_currency]

View File

@ -97,7 +97,7 @@ class Deferred_Item(object):
if base_amount + already_booked_amount > self.base_net_amount:
base_amount = self.base_net_amount - already_booked_amount
if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
if get_first_day(start_date) != start_date or get_last_day(end_date) != end_date:
partial_month = flt(date_diff(end_date, start_date)) / flt(
date_diff(get_last_day(end_date), get_first_day(start_date))
)

View File

@ -8,7 +8,17 @@ import re
import frappe
from frappe import _
from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate
from frappe.utils import (
add_days,
add_months,
cint,
cstr,
flt,
formatdate,
get_first_day,
getdate,
today,
)
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
@ -43,6 +53,8 @@ def get_period_list(
year_start_date = getdate(period_start_date)
year_end_date = getdate(period_end_date)
year_end_date = getdate(today()) if year_end_date > getdate(today()) else year_end_date
months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
period_list = []

View File

@ -203,7 +203,7 @@ def get_gl_entries(filters, accounting_dimensions):
voucher_type, voucher_no, {dimension_fields}
cost_center, project, {transaction_currency_fields}
against_voucher_type, against_voucher, account_currency,
against, is_opening, creation {select_fields}
against_link, against, is_opening, creation {select_fields}
from `tabGL Entry`
where company=%(company)s {conditions}
{order_by_statement}
@ -238,6 +238,9 @@ def get_conditions(filters):
if filters.get("voucher_no"):
conditions.append("voucher_no=%(voucher_no)s")
if filters.get("voucher_no_not_in"):
conditions.append("voucher_no not in %(voucher_no_not_in)s")
if filters.get("group_by") == "Group by Party" and not filters.get("party_type"):
conditions.append("party_type in ('Customer', 'Supplier')")
@ -289,7 +292,8 @@ def get_conditions(filters):
if accounting_dimensions:
for dimension in accounting_dimensions:
if not dimension.disabled:
# Ignore 'Finance Book' set up as dimension in below logic, as it is already handled in above section
if not dimension.disabled and dimension.document_type != "Finance Book":
if filters.get(dimension.fieldname):
if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
filters[dimension.fieldname] = get_dimension_with_children(
@ -391,6 +395,7 @@ def initialize_gle_map(gl_entries, filters):
group_by = group_by_field(filters.get("group_by"))
for gle in gl_entries:
gle.against = gle.get("against_link") or gle.get("against")
gle_map.setdefault(gle.get(group_by), _dict(totals=get_totals_dict(), entries=[]))
return gle_map

View File

@ -170,7 +170,7 @@ def set_total(node, value, complete_list, totals):
totals[node["account"]] += value
parent = node["parent_account"]
if not parent == "":
if parent != "":
return set_total(
next(item for item in complete_list if item["account"] == parent), value, complete_list, totals
)

View File

@ -695,7 +695,7 @@ class GrossProfitGenerator(object):
def get_average_buying_rate(self, row, item_code):
args = row
if not item_code in self.average_buying_rate:
if item_code not in self.average_buying_rate:
args.update(
{
"voucher_type": row.parenttype,

View File

@ -89,6 +89,8 @@ def _execute(filters=None, additional_table_columns=None):
"payable_account": inv.credit_to,
"mode_of_payment": inv.mode_of_payment,
"project": ", ".join(project) if inv.doctype == "Purchase Invoice" else inv.project,
"bill_no": inv.bill_no,
"bill_date": inv.bill_date,
"remarks": inv.remarks,
"purchase_order": ", ".join(purchase_order),
"purchase_receipt": ", ".join(purchase_receipt),

View File

@ -154,7 +154,7 @@ def get_gle_map(documents):
)
for d in gle:
if not d.voucher_no in gle_map:
if d.voucher_no not in gle_map:
gle_map[d.voucher_no] = [d]
else:
gle_map[d.voucher_no].append(d)
@ -345,21 +345,16 @@ def get_tds_docs_query(filters, bank_accounts, tds_accounts):
if filters.get("party"):
party = [filters.get("party")]
query = query.where(
((gle.account.isin(tds_accounts) & gle.against.isin(party)))
| ((gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party")))
| gle.party.isin(party)
jv_condition = gle.against.isin(party) | (
(gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party"))
)
else:
party = frappe.get_all(filters.get("party_type"), pluck="name")
query = query.where(
((gle.account.isin(tds_accounts) & gle.against.isin(party)))
| (
(gle.voucher_type == "Journal Entry")
& ((gle.party_type == filters.get("party_type")) | (gle.party_type == ""))
)
| gle.party.isin(party)
jv_condition = gle.against.isin(party) | (
(gle.voucher_type == "Journal Entry")
& ((gle.party_type == filters.get("party_type")) | (gle.party_type == ""))
)
query = query.where((gle.account.isin(tds_accounts) & jv_condition) | gle.party.isin(party))
return query

View File

@ -642,6 +642,7 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
new_row.set("reference_name", d["against_voucher"])
new_row.against_account = cstr(jv_detail.against_account)
new_row.against_account_link = cstr(jv_detail.against_account)
new_row.is_advance = cstr(jv_detail.is_advance)
new_row.docstatus = 1
@ -662,8 +663,10 @@ def update_reference_in_payment_entry(
"total_amount": d.grand_total,
"outstanding_amount": d.outstanding_amount,
"allocated_amount": d.allocated_amount,
"exchange_rate": d.exchange_rate if d.exchange_gain_loss else payment_entry.get_exchange_rate(),
"exchange_gain_loss": d.exchange_gain_loss,
"exchange_rate": d.exchange_rate
if d.difference_amount is not None
else payment_entry.get_exchange_rate(),
"exchange_gain_loss": d.difference_amount,
"account": d.account,
}
@ -1062,11 +1065,11 @@ def get_outstanding_invoices(
if (
min_outstanding
and max_outstanding
and not (outstanding_amount >= min_outstanding and outstanding_amount <= max_outstanding)
and (outstanding_amount < min_outstanding or outstanding_amount > max_outstanding)
):
continue
if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices:
if d.voucher_type != "Purchase Invoice" or d.voucher_no not in held_invoices:
outstanding_invoices.append(
frappe._dict(
{

View File

@ -313,7 +313,7 @@ class Asset(AccountsController):
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
if is_cwip_accounting_enabled(self.asset_category):
if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice):
if not self.is_existing_asset and not self.purchase_receipt and not self.purchase_invoice:
frappe.throw(
_("Please create purchase receipt or purchase invoice for the item {0}").format(
self.item_code
@ -689,7 +689,9 @@ class Asset(AccountsController):
self.get_gl_dict(
{
"account": cwip_account,
"against_type": "Account",
"against": fixed_asset_account,
"against_link": fixed_asset_account,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
"credit": self.purchase_receipt_amount,
@ -704,7 +706,9 @@ class Asset(AccountsController):
self.get_gl_dict(
{
"account": fixed_asset_account,
"against_type": "Account",
"against": cwip_account,
"against_link": cwip_account,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
"debit": self.purchase_receipt_amount,

View File

@ -251,7 +251,16 @@ class TestAsset(AssetSetup):
flt(18000.0 + pro_rata_amount, asset.precision("gross_purchase_amount")),
0.0,
),
("_Test Fixed Asset - _TC", 0.0, 100000.0),
(
"_Test Fixed Asset - _TC",
0.0,
flt(18000.0 + pro_rata_amount, asset.precision("gross_purchase_amount")),
),
(
"_Test Fixed Asset - _TC",
0.0,
flt(82000.0 - pro_rata_amount, asset.precision("gross_purchase_amount")),
),
(
"_Test Gain/Loss on Asset Disposal - _TC",
flt(82000.0 - pro_rata_amount, asset.precision("gross_purchase_amount")),

View File

@ -485,7 +485,9 @@ class AssetCapitalization(StockController):
self.get_gl_dict(
{
"account": account,
"against_type": "Account",
"against": target_account,
"against_link": target_account,
"cost_center": item_row.cost_center,
"project": item_row.get("project") or self.get("project"),
"remarks": self.get("remarks") or "Accounting Entry for Stock",
@ -526,7 +528,9 @@ class AssetCapitalization(StockController):
self.set_consumed_asset_status(asset)
for gle in fixed_asset_gl_entries:
gle["against_type"] = "Account"
gle["against"] = target_account
gle["against_link"] = target_account
gl_entries.append(self.get_gl_dict(gle, item=item))
target_against.add(gle["account"])
@ -542,7 +546,9 @@ class AssetCapitalization(StockController):
self.get_gl_dict(
{
"account": item_row.expense_account,
"against_type": "Account",
"against": target_account,
"against_link": target_account,
"cost_center": item_row.cost_center,
"project": item_row.get("project") or self.get("project"),
"remarks": self.get("remarks") or "Accounting Entry for Stock",
@ -553,41 +559,46 @@ class AssetCapitalization(StockController):
)
def get_gl_entries_for_target_item(self, gl_entries, target_against, precision):
if self.target_is_fixed_asset:
# Capitalization
gl_entries.append(
self.get_gl_dict(
{
"account": self.target_fixed_asset_account,
"against": ", ".join(target_against),
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"debit": flt(self.total_value, precision),
"cost_center": self.get("cost_center"),
},
item=self,
)
)
else:
# Target Stock Item
sle_list = self.sle_map.get(self.name)
for sle in sle_list:
stock_value_difference = flt(sle.stock_value_difference, precision)
account = self.warehouse_account[sle.warehouse]["account"]
for target_account in target_against:
if self.target_is_fixed_asset:
# Capitalization
gl_entries.append(
self.get_gl_dict(
{
"account": account,
"against": ", ".join(target_against),
"cost_center": self.cost_center,
"project": self.get("project"),
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"debit": stock_value_difference,
"account": self.target_fixed_asset_account,
"against_type": "Account",
"against": target_account,
"against_link": target_account,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"debit": flt(self.total_value, precision) / len(target_against),
"cost_center": self.get("cost_center"),
},
self.warehouse_account[sle.warehouse]["account_currency"],
item=self,
)
)
else:
# Target Stock Item
sle_list = self.sle_map.get(self.name)
for sle in sle_list:
stock_value_difference = flt(sle.stock_value_difference, precision)
account = self.warehouse_account[sle.warehouse]["account"]
gl_entries.append(
self.get_gl_dict(
{
"account": account,
"against_type": "Account",
"against": target_account,
"against_link": target_account,
"cost_center": self.cost_center,
"project": self.get("project"),
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"debit": stock_value_difference / len(target_against),
},
self.warehouse_account[sle.warehouse]["account_currency"],
item=self,
)
)
def create_target_asset(self):
if (

View File

@ -98,12 +98,12 @@ class TestAssetCapitalization(unittest.TestCase):
# Test General Ledger Entries
expected_gle = {
"_Test Fixed Asset - TCP1": 3000,
"_Test Fixed Asset - TCP1": 2999.99,
"Expenses Included In Asset Valuation - TCP1": -1000,
"_Test Warehouse - TCP1": -2000,
"Round Off - TCP1": 0.01,
}
actual_gle = get_actual_gle_dict(asset_capitalization.name)
self.assertEqual(actual_gle, expected_gle)
# Test Stock Ledger Entries
@ -189,9 +189,10 @@ class TestAssetCapitalization(unittest.TestCase):
# Test General Ledger Entries
default_expense_account = frappe.db.get_value("Company", company, "default_expense_account")
expected_gle = {
"_Test Fixed Asset - _TC": 3000,
"_Test Fixed Asset - _TC": 2999.99,
"Expenses Included In Asset Valuation - _TC": -1000,
default_expense_account: -2000,
"Round Off - _TC": 0.01,
}
actual_gle = get_actual_gle_dict(asset_capitalization.name)
@ -376,9 +377,10 @@ class TestAssetCapitalization(unittest.TestCase):
# Test General Ledger Entries
expected_gle = {
"_Test Warehouse - TCP1": consumed_asset_value_before_disposal,
"_Test Accumulated Depreciations - TCP1": accumulated_depreciation,
"_Test Fixed Asset - TCP1": -consumed_asset_purchase_value,
"_Test Warehouse - TCP1": consumed_asset_value_before_disposal - 0.01,
"Round Off - TCP1": 0.01,
}
actual_gle = get_actual_gle_dict(asset_capitalization.name)
self.assertEqual(actual_gle, expected_gle)

View File

@ -340,6 +340,10 @@ class AssetDepreciationSchedule(Document):
n == 0
and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata)
and not self.opening_accumulated_depreciation
and get_updated_rate_of_depreciation_for_wdv_and_dd(
asset_doc, value_after_depreciation, row, False
)
== row.rate_of_depreciation
):
from_date = add_days(
asset_doc.available_for_use_date, -1
@ -605,7 +609,9 @@ def get_depreciation_amount(
@erpnext.allow_regional
def get_updated_rate_of_depreciation_for_wdv_and_dd(asset, depreciable_value, fb_row):
def get_updated_rate_of_depreciation_for_wdv_and_dd(
asset, depreciable_value, fb_row, show_msg=True
):
return fb_row.rate_of_depreciation

View File

@ -84,7 +84,7 @@ class AssetMovement(Document):
frappe.throw(_("Source and Target Location cannot be same"))
if self.purpose == "Receipt":
if not (d.source_location) and not (d.target_location or d.to_employee):
if not (d.source_location) and not d.target_location and not d.to_employee:
frappe.throw(
_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset)
)

View File

@ -277,7 +277,9 @@ class AssetRepair(AccountsController):
"account": fixed_asset_account,
"debit": self.repair_cost,
"debit_in_account_currency": self.repair_cost,
"against_type": "Account",
"against": pi_expense_account,
"against_link": pi_expense_account,
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
@ -296,7 +298,9 @@ class AssetRepair(AccountsController):
"account": pi_expense_account,
"credit": self.repair_cost,
"credit_in_account_currency": self.repair_cost,
"against_type": "Account",
"against": fixed_asset_account,
"against_link": fixed_asset_account,
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
@ -330,7 +334,9 @@ class AssetRepair(AccountsController):
"account": item.expense_account or default_expense_account,
"credit": item.amount,
"credit_in_account_currency": item.amount,
"against_type": "Account",
"against": fixed_asset_account,
"against_link": fixed_asset_account,
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
@ -347,7 +353,9 @@ class AssetRepair(AccountsController):
"account": fixed_asset_account,
"debit": item.amount,
"debit_in_account_currency": item.amount,
"against_type": "Account",
"against": item.expense_account or default_expense_account,
"against_link": item.expense_account or default_expense_account,
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,

View File

@ -214,7 +214,7 @@ frappe.ui.form.on("Purchase Order Item", {
}
},
fg_item_qty: async function(frm, cdt, cdn) {
qty: async function (frm, cdt, cdn) {
if (frm.doc.is_subcontracted && !frm.doc.is_old_subcontracting_flow) {
var row = locals[cdt][cdn];
@ -222,7 +222,7 @@ frappe.ui.form.on("Purchase Order Item", {
var result = await frm.events.get_subcontracting_boms_for_finished_goods(row.fg_item)
if (result.message && row.item_code == result.message.service_item && row.uom == result.message.service_item_uom) {
frappe.model.set_value(cdt, cdn, "qty", flt(row.fg_item_qty) * flt(result.message.conversion_factor));
frappe.model.set_value(cdt, cdn, "fg_item_qty", flt(row.qty) / flt(result.message.conversion_factor));
}
}
}

View File

@ -119,6 +119,15 @@ class RequestforQuotation(BuyingController):
supplier.quote_status = "Pending"
self.send_to_supplier()
def before_print(self, settings=None):
"""Use the first suppliers data to render the print preview."""
if self.vendor or not self.suppliers:
# If a specific supplier is already set, via Tools > Download PDF,
# we don't want to override it.
return
self.update_supplier_part_no(self.suppliers[0].supplier)
def on_cancel(self):
self.db_set("status", "Cancelled")

View File

@ -114,7 +114,7 @@ def prepare_data(data, filters):
if filters.get("group_by_po"):
po_name = row["purchase_order"]
if not po_name in purchase_order_map:
if po_name not in purchase_order_map:
# create an entry
row_copy = copy.deepcopy(row)
purchase_order_map[po_name] = row_copy

View File

@ -110,7 +110,7 @@ def prepare_data(data, filters):
for row in data:
# item wise map for charts
if not row["item_code"] in item_qty_map:
if row["item_code"] not in item_qty_map:
item_qty_map[row["item_code"]] = {
"qty": flt(row["stock_qty"], precision),
"stock_qty": flt(row["stock_qty"], precision),
@ -127,7 +127,7 @@ def prepare_data(data, filters):
if filters.get("group_by_mr"):
# consolidated material request map for group by filter
if not row["material_request"] in material_request_map:
if row["material_request"] not in material_request_map:
# create an entry with mr as key
row_copy = copy.deepcopy(row)
material_request_map[row["material_request"]] = row_copy

View File

@ -126,7 +126,7 @@ def prepare_data(supplier_quotation_data, filters):
# map for chart preparation of the form {'supplier1': {'qty': 'price'}}
supplier = data.get("supplier_name")
if filters.get("item_code"):
if not supplier in supplier_qty_price_map:
if supplier not in supplier_qty_price_map:
supplier_qty_price_map[supplier] = {}
supplier_qty_price_map[supplier][row["qty"]] = row["price"]
@ -169,7 +169,7 @@ def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map):
for supplier in suppliers:
entry = supplier_qty_price_map[supplier]
for qty in qty_list:
if not qty in data_points_map:
if qty not in data_points_map:
data_points_map[qty] = []
if qty in entry:
data_points_map[qty].append(entry[qty])

View File

@ -166,6 +166,7 @@ class AccountsController(TransactionBase):
self.disable_pricing_rule_on_internal_transfer()
self.disable_tax_included_prices_for_internal_transfer()
self.set_incoming_rate()
self.init_internal_values()
if self.meta.get_field("currency"):
self.calculate_taxes_and_totals()
@ -225,6 +226,16 @@ class AccountsController(TransactionBase):
self.set_total_in_words()
def init_internal_values(self):
# init all the internal values as 0 on sa
if self.docstatus.is_draft():
# TODO: Add all such pending values here
fields = ["billed_amt", "delivered_qty"]
for item in self.get("items"):
for field in fields:
if hasattr(item, field):
item.set(field, 0)
def before_cancel(self):
validate_einvoice_fields(self)
@ -292,6 +303,7 @@ class AccountsController(TransactionBase):
def on_trash(self):
self._remove_references_in_repost_doctypes()
self._remove_references_in_unreconcile()
self.remove_serial_and_batch_bundle()
# delete sl and gl entries on deletion of transaction
if frappe.db.get_single_value("Accounts Settings", "delete_linked_ledger_entries"):
@ -307,6 +319,15 @@ class AccountsController(TransactionBase):
(self.doctype, self.name),
)
def remove_serial_and_batch_bundle(self):
bundles = frappe.get_all(
"Serial and Batch Bundle",
filters={"voucher_type": self.doctype, "voucher_no": self.name, "docstatus": ("!=", 1)},
)
for bundle in bundles:
frappe.delete_doc("Serial and Batch Bundle", bundle.name)
def validate_deferred_income_expense_account(self):
field_map = {
"Sales Invoice": "deferred_revenue_account",
@ -1099,6 +1120,7 @@ class AccountsController(TransactionBase):
)
credit_or_debit = "credit" if self.doctype == "Purchase Invoice" else "debit"
against_type = "Supplier" if self.doctype == "Purchase Invoice" else "Customer"
against = self.supplier if self.doctype == "Purchase Invoice" else self.customer
if precision_loss:
@ -1106,7 +1128,9 @@ class AccountsController(TransactionBase):
self.get_gl_dict(
{
"account": round_off_account,
"against_type": against_type,
"against": against,
"against_link": against,
credit_or_debit: precision_loss,
"cost_center": round_off_cost_center
if self.use_company_roundoff_cost_center
@ -1455,11 +1479,13 @@ class AccountsController(TransactionBase):
if self.doctype == "Purchase Invoice":
dr_or_cr = "credit"
rev_dr_cr = "debit"
against_type = "Supplier"
supplier_or_customer = self.supplier
else:
dr_or_cr = "debit"
rev_dr_cr = "credit"
against_type = "Customer"
supplier_or_customer = self.customer
if enable_discount_accounting:
@ -1484,7 +1510,9 @@ class AccountsController(TransactionBase):
self.get_gl_dict(
{
"account": item.discount_account,
"against_type": against_type,
"against": supplier_or_customer,
"against_link": supplier_or_customer,
dr_or_cr: flt(
discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
),
@ -1502,7 +1530,9 @@ class AccountsController(TransactionBase):
self.get_gl_dict(
{
"account": income_or_expense_account,
"against_type": against_type,
"against": supplier_or_customer,
"against_link": supplier_or_customer,
rev_dr_cr: flt(
discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
),
@ -1525,7 +1555,9 @@ class AccountsController(TransactionBase):
self.get_gl_dict(
{
"account": self.additional_discount_account,
"against_type": against_type,
"against": supplier_or_customer,
"against_link": supplier_or_customer,
dr_or_cr: self.base_discount_amount,
"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
},

View File

@ -381,7 +381,11 @@ class BuyingController(SubcontractingController):
rate = flt(outgoing_rate * (d.conversion_factor or 1), d.precision("rate"))
else:
field = "incoming_rate" if self.get("is_internal_supplier") else "rate"
field = (
"incoming_rate"
if self.get("is_internal_supplier") and not self.doctype == "Purchase Order"
else "rate"
)
rate = flt(
frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), field)
* (d.conversion_factor or 1),

View File

@ -56,10 +56,24 @@ def make_variant_based_on_manufacturer(template, manufacturer, manufacturer_part
copy_attributes_to_variant(template, variant)
variant.manufacturer = manufacturer
variant.manufacturer_part_no = manufacturer_part_no
variant.item_code = append_number_if_name_exists("Item", template.name)
variant.flags.ignore_mandatory = True
variant.save()
if not frappe.db.exists(
"Item Manufacturer", {"item_code": variant.name, "manufacturer": manufacturer}
):
manufacturer_doc = frappe.new_doc("Item Manufacturer")
manufacturer_doc.update(
{
"item_code": variant.name,
"manufacturer": manufacturer,
"manufacturer_part_no": manufacturer_part_no,
}
)
manufacturer_doc.flags.ignore_mandatory = True
manufacturer_doc.save(ignore_permissions=True)
return variant

View File

@ -222,7 +222,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
searchfields = meta.get_search_fields()
columns = ""
extra_searchfields = [field for field in searchfields if not field in ["name", "description"]]
extra_searchfields = [field for field in searchfields if field not in ["name", "description"]]
if extra_searchfields:
columns += ", " + ", ".join(extra_searchfields)
@ -233,8 +233,13 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
searchfields = searchfields + [
field
for field in [searchfield or "name", "item_code", "item_group", "item_name"]
if not field in searchfields
for field in [
searchfield or "name",
"item_code",
"item_group",
"item_name",
]
if field not in searchfields
]
searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
@ -872,7 +877,7 @@ def get_fields(doctype, fields=None):
meta = frappe.get_meta(doctype)
fields.extend(meta.get_search_fields())
if meta.title_field and not meta.title_field.strip() in fields:
if meta.title_field and meta.title_field.strip() not in fields:
fields.insert(1, meta.title_field.strip())
return unique(fields)

View File

@ -8,6 +8,8 @@ from frappe.model.meta import get_field_precision
from frappe.utils import flt, format_datetime, get_datetime
import erpnext
from erpnext.stock.serial_batch_bundle import get_batches_from_bundle
from erpnext.stock.serial_batch_bundle import get_serial_nos as get_serial_nos_from_bundle
from erpnext.stock.utils import get_incoming_rate
@ -69,8 +71,6 @@ def validate_return_against(doc):
def validate_returned_items(doc):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
valid_items = frappe._dict()
select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor"
@ -123,26 +123,6 @@ def validate_returned_items(doc):
)
)
elif ref.batch_no and d.batch_no not in ref.batch_no:
frappe.throw(
_("Row # {0}: Batch No must be same as {1} {2}").format(
d.idx, doc.doctype, doc.return_against
)
)
elif ref.serial_no:
if d.qty and not d.serial_no:
frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
else:
serial_nos = get_serial_nos(d.serial_no)
for s in serial_nos:
if s not in ref.serial_no:
frappe.throw(
_("Row # {0}: Serial No {1} does not match with {2} {3}").format(
d.idx, s, doc.doctype, doc.return_against
)
)
if (
warehouse_mandatory
and not d.get("warehouse")
@ -397,71 +377,92 @@ def make_return_doc(
else:
doc.run_method("calculate_taxes_and_totals")
def update_item(source_doc, target_doc, source_parent):
def update_serial_batch_no(source_doc, target_doc, source_parent, item_details, qty_field):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.stock.serial_batch_bundle import SerialBatchCreation
target_doc.qty = -1 * source_doc.qty
item_details = frappe.get_cached_value(
"Item", source_doc.item_code, ["has_batch_no", "has_serial_no"], as_dict=1
)
returned_serial_nos = []
if source_doc.get("serial_and_batch_bundle"):
if item_details.has_serial_no:
returned_serial_nos = get_returned_serial_nos(source_doc, source_parent)
returned_batches = frappe._dict()
serial_and_batch_field = (
"serial_and_batch_bundle" if qty_field == "stock_qty" else "rejected_serial_and_batch_bundle"
)
old_serial_no_field = "serial_no" if qty_field == "stock_qty" else "rejected_serial_no"
old_batch_no_field = "batch_no"
type_of_transaction = "Inward"
if (
frappe.db.get_value(
"Serial and Batch Bundle", source_doc.serial_and_batch_bundle, "type_of_transaction"
)
== "Inward"
):
type_of_transaction = "Outward"
cls_obj = SerialBatchCreation(
{
"type_of_transaction": type_of_transaction,
"serial_and_batch_bundle": source_doc.serial_and_batch_bundle,
"returned_against": source_doc.name,
"item_code": source_doc.item_code,
"returned_serial_nos": returned_serial_nos,
}
)
cls_obj.duplicate_package()
if cls_obj.serial_and_batch_bundle:
target_doc.serial_and_batch_bundle = cls_obj.serial_and_batch_bundle
if source_doc.get("rejected_serial_and_batch_bundle"):
if (
source_doc.get(serial_and_batch_field)
or source_doc.get(old_serial_no_field)
or source_doc.get(old_batch_no_field)
):
if item_details.has_serial_no:
returned_serial_nos = get_returned_serial_nos(
source_doc, source_parent, serial_no_field="rejected_serial_and_batch_bundle"
source_doc, source_parent, serial_no_field=serial_and_batch_field
)
else:
returned_batches = get_returned_batches(
source_doc, source_parent, batch_no_field=serial_and_batch_field
)
type_of_transaction = "Inward"
if (
if source_doc.get(serial_and_batch_field) and (
frappe.db.get_value(
"Serial and Batch Bundle", source_doc.rejected_serial_and_batch_bundle, "type_of_transaction"
"Serial and Batch Bundle", source_doc.get(serial_and_batch_field), "type_of_transaction"
)
== "Inward"
):
type_of_transaction = "Outward"
elif source_parent.doctype in [
"Purchase Invoice",
"Purchase Receipt",
"Subcontracting Receipt",
]:
type_of_transaction = "Outward"
cls_obj = SerialBatchCreation(
{
"type_of_transaction": type_of_transaction,
"serial_and_batch_bundle": source_doc.rejected_serial_and_batch_bundle,
"serial_and_batch_bundle": source_doc.get(serial_and_batch_field),
"returned_against": source_doc.name,
"item_code": source_doc.item_code,
"returned_serial_nos": returned_serial_nos,
"voucher_type": source_parent.doctype,
"do_not_submit": True,
"warehouse": source_doc.warehouse,
"has_serial_no": item_details.has_serial_no,
"has_batch_no": item_details.has_batch_no,
}
)
cls_obj.duplicate_package()
if cls_obj.serial_and_batch_bundle:
target_doc.serial_and_batch_bundle = cls_obj.serial_and_batch_bundle
serial_nos = []
batches = frappe._dict()
if source_doc.get(old_batch_no_field):
batches = frappe._dict({source_doc.batch_no: source_doc.get(qty_field)})
elif source_doc.get(old_serial_no_field):
serial_nos = get_serial_nos(source_doc.get(old_serial_no_field))
elif source_doc.get(serial_and_batch_field):
if item_details.has_serial_no:
serial_nos = get_serial_nos_from_bundle(source_doc.get(serial_and_batch_field))
else:
batches = get_batches_from_bundle(source_doc.get(serial_and_batch_field))
if serial_nos:
cls_obj.serial_nos = sorted(list(set(serial_nos) - set(returned_serial_nos)))
elif batches:
for batch in batches:
if batch in returned_batches:
batches[batch] -= flt(returned_batches.get(batch))
cls_obj.batches = batches
if source_doc.get(serial_and_batch_field):
cls_obj.duplicate_package()
if cls_obj.serial_and_batch_bundle:
target_doc.set(serial_and_batch_field, cls_obj.serial_and_batch_bundle)
else:
target_doc.set(serial_and_batch_field, cls_obj.make_serial_and_batch_bundle().name)
def update_item(source_doc, target_doc, source_parent):
target_doc.qty = -1 * source_doc.qty
if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
returned_qty_map = get_returned_qty_map_for_row(
source_parent.name, source_parent.supplier, source_doc.name, doctype
@ -561,6 +562,17 @@ def make_return_doc(
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
item_details = frappe.get_cached_value(
"Item", source_doc.item_code, ["has_batch_no", "has_serial_no"], as_dict=1
)
if not item_details.has_batch_no and not item_details.has_serial_no:
return
for qty_field in ["stock_qty", "rejected_qty"]:
if target_doc.get(qty_field):
update_serial_batch_no(source_doc, target_doc, source_parent, item_details, qty_field)
def update_terms(source_doc, target_doc, source_parent):
target_doc.payment_amount = -source_doc.payment_amount
@ -716,6 +728,9 @@ def get_returned_serial_nos(
[parent_doc.doctype, "docstatus", "=", 1],
]
if serial_no_field == "rejected_serial_and_batch_bundle":
filters.append([child_doc.doctype, "rejected_qty", ">", 0])
# Required for POS Invoice
if ignore_voucher_detail_no:
filters.append([child_doc.doctype, "name", "!=", ignore_voucher_detail_no])
@ -723,9 +738,57 @@ def get_returned_serial_nos(
ids = []
for row in frappe.get_all(parent_doc.doctype, fields=fields, filters=filters):
ids.append(row.get("serial_and_batch_bundle"))
if row.get(old_field):
if row.get(old_field) and not row.get(serial_no_field):
serial_nos.extend(get_serial_nos_from_serial_no(row.get(old_field)))
serial_nos.extend(get_serial_nos(ids))
if ids:
serial_nos.extend(get_serial_nos(ids))
return serial_nos
def get_returned_batches(
child_doc, parent_doc, batch_no_field=None, ignore_voucher_detail_no=None
):
from erpnext.stock.serial_batch_bundle import get_batches_from_bundle
batches = frappe._dict()
old_field = "batch_no"
if not batch_no_field:
batch_no_field = "serial_and_batch_bundle"
return_ref_field = frappe.scrub(child_doc.doctype)
if child_doc.doctype == "Delivery Note Item":
return_ref_field = "dn_detail"
fields = [
f"`{'tab' + child_doc.doctype}`.`{batch_no_field}`",
f"`{'tab' + child_doc.doctype}`.`batch_no`",
f"`{'tab' + child_doc.doctype}`.`stock_qty`",
]
filters = [
[parent_doc.doctype, "return_against", "=", parent_doc.name],
[parent_doc.doctype, "is_return", "=", 1],
[child_doc.doctype, return_ref_field, "=", child_doc.name],
[parent_doc.doctype, "docstatus", "=", 1],
]
if batch_no_field == "rejected_serial_and_batch_bundle":
filters.append([child_doc.doctype, "rejected_qty", ">", 0])
# Required for POS Invoice
if ignore_voucher_detail_no:
filters.append([child_doc.doctype, "name", "!=", ignore_voucher_detail_no])
ids = []
for row in frappe.get_all(parent_doc.doctype, fields=fields, filters=filters):
ids.append(row.get("serial_and_batch_bundle"))
if row.get(old_field) and not row.get(batch_no_field):
batches.setdefault(row.get(old_field), row.get("stock_qty"))
if ids:
batches.update(get_batches_from_bundle(ids))
return batches

View File

@ -12,7 +12,7 @@ from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
from erpnext.controllers.stock_controller import StockController
from erpnext.stock.doctype.item.item import set_item_default
from erpnext.stock.get_item_details import get_bin_details, get_conversion_factor
from erpnext.stock.utils import get_incoming_rate
from erpnext.stock.utils import get_incoming_rate, get_valuation_method
class SellingController(StockController):
@ -308,6 +308,8 @@ class SellingController(StockController):
"warehouse": p.warehouse or d.warehouse,
"item_code": p.item_code,
"qty": flt(p.qty),
"serial_no": p.serial_no if self.docstatus == 2 else None,
"batch_no": p.batch_no if self.docstatus == 2 else None,
"uom": p.uom,
"serial_and_batch_bundle": p.serial_and_batch_bundle
or get_serial_and_batch_bundle(p, self),
@ -330,6 +332,8 @@ class SellingController(StockController):
"warehouse": d.warehouse,
"item_code": d.item_code,
"qty": d.stock_qty,
"serial_no": d.serial_no if self.docstatus == 2 else None,
"batch_no": d.batch_no if self.docstatus == 2 else None,
"uom": d.uom,
"stock_uom": d.stock_uom,
"conversion_factor": d.conversion_factor,
@ -428,11 +432,13 @@ class SellingController(StockController):
items = self.get("items") + (self.get("packed_items") or [])
for d in items:
if not self.get("return_against"):
if not self.get("return_against") or (
get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return")
):
# Get incoming rate based on original item cost based on valuation method
qty = flt(d.get("stock_qty") or d.get("actual_qty"))
if not (self.get("is_return") and d.incoming_rate):
if not d.incoming_rate:
d.incoming_rate = get_incoming_rate(
{
"item_code": d.item_code,

View File

@ -162,7 +162,9 @@ class StockController(AccountsController):
self.get_gl_dict(
{
"account": warehouse_account[sle.warehouse]["account"],
"against_type": "Account",
"against": expense_account,
"against_link": expense_account,
"cost_center": item_row.cost_center,
"project": item_row.project or self.get("project"),
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@ -178,7 +180,9 @@ class StockController(AccountsController):
self.get_gl_dict(
{
"account": expense_account,
"against_type": "Account",
"against": warehouse_account[sle.warehouse]["account"],
"against_link": warehouse_account[sle.warehouse]["account"],
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": -1 * flt(sle.stock_value_difference, precision),
@ -210,7 +214,9 @@ class StockController(AccountsController):
self.get_gl_dict(
{
"account": expense_account,
"against_type": "Account",
"against": warehouse_asset_account,
"against_link": warehouse_asset_account,
"cost_center": item_row.cost_center,
"project": item_row.project or self.get("project"),
"remarks": _("Rounding gain/loss Entry for Stock Transfer"),
@ -226,7 +232,9 @@ class StockController(AccountsController):
self.get_gl_dict(
{
"account": warehouse_asset_account,
"against_type": "Account",
"against": expense_account,
"against_link": expense_account,
"cost_center": item_row.cost_center,
"remarks": _("Rounding gain/loss Entry for Stock Transfer"),
"credit": sle_rounding_diff,
@ -455,6 +463,12 @@ class StockController(AccountsController):
sl_dict.update(args)
self.update_inventory_dimensions(d, sl_dict)
if self.docstatus == 2:
# To handle denormalized serial no records, will br deprecated in v16
for field in ["serial_no", "batch_no"]:
if d.get(field):
sl_dict[field] = d.get(field)
return sl_dict
def update_inventory_dimensions(self, row, sl_dict) -> None:
@ -642,7 +656,7 @@ class StockController(AccountsController):
)
qa_docstatus = frappe.db.get_value("Quality Inspection", row.quality_inspection, "docstatus")
if not qa_docstatus == 1:
if qa_docstatus != 1:
link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection)
msg = (
f"Row #{row.idx}: Quality Inspection {link} is not submitted for the item: {row.item_code}"
@ -826,6 +840,7 @@ class StockController(AccountsController):
credit,
remarks,
against_account,
against_type="Account",
debit_in_account_currency=None,
credit_in_account_currency=None,
account_currency=None,
@ -840,7 +855,9 @@ class StockController(AccountsController):
"cost_center": cost_center,
"debit": debit,
"credit": credit,
"against_type": against_type,
"against": against_account,
"against_link": against_account,
"remarks": remarks,
}

View File

@ -55,7 +55,7 @@ class Appointment(Document):
"Appointment", filters={"scheduled_time": self.scheduled_time}
)
number_of_agents = frappe.db.get_single_value("Appointment Booking Settings", "number_of_agents")
if not number_of_agents == 0:
if number_of_agents != 0:
if number_of_appointments_in_same_slot >= number_of_agents:
frappe.throw(_("Time slot is not available"))
# Link lead
@ -110,7 +110,7 @@ class Appointment(Document):
cal_event.save(ignore_permissions=True)
def set_verified(self, email):
if not email == self.customer_email:
if email != self.customer_email:
frappe.throw(_("Email verification failed."))
# Create new lead
self.create_lead_and_link()

View File

@ -516,7 +516,7 @@
"idx": 5,
"image_field": "image",
"links": [],
"modified": "2023-08-28 22:28:00.104413",
"modified": "2023-12-01 18:46:49.468526",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
@ -577,6 +577,7 @@
],
"search_fields": "lead_name,lead_owner,status",
"sender_field": "email_id",
"sender_name_field": "lead_name",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",

View File

@ -16,6 +16,7 @@ from frappe.utils import comma_and, get_link_to_form, has_gravatar, validate_ema
from erpnext.accounts.party import set_taxes
from erpnext.controllers.selling_controller import SellingController
from erpnext.crm.utils import CRMNote, copy_comments, link_communications, link_open_events
from erpnext.selling.doctype.customer.customer import parse_full_name
class Lead(SellingController, CRMNote):
@ -105,7 +106,7 @@ class Lead(SellingController, CRMNote):
if self.source == "Existing Customer" and self.customer:
contact = frappe.db.get_value(
"Dynamic Link",
{"link_doctype": "Customer", "link_name": self.customer},
{"link_doctype": "Customer", "parenttype": "Contact", "link_name": self.customer},
"parent",
)
if contact:
@ -113,6 +114,10 @@ class Lead(SellingController, CRMNote):
return
self.contact_doc = self.create_contact()
# leads created by email inbox only have the full name set
if self.lead_name and not any([self.first_name, self.middle_name, self.last_name]):
self.first_name, self.middle_name, self.last_name = parse_full_name(self.lead_name)
def after_insert(self):
self.link_to_contact()

View File

@ -16,7 +16,7 @@ def validate_webhooks_request(doctype, hmac_key, secret_key="secret"):
hmac.new(settings.get(secret_key).encode("utf8"), frappe.request.data, hashlib.sha256).digest()
)
if frappe.request.data and not sig == bytes(frappe.get_request_header(hmac_key).encode()):
if frappe.request.data and sig != bytes(frappe.get_request_header(hmac_key).encode()):
frappe.throw(_("Unverified Webhook Data"))
frappe.set_user(settings.modified_by)

View File

@ -637,6 +637,7 @@ additional_timeline_content = {
extend_bootinfo = [
"erpnext.support.doctype.service_level_agreement.service_level_agreement.add_sla_doctypes",
"erpnext.startup.boot.bootinfo",
]

View File

@ -89,7 +89,7 @@ def make_order(source_name):
def update_item(source, target, source_parent):
target_qty = source.get("qty") - source.get("ordered_qty")
target.qty = target_qty if not flt(target_qty) < 0 else 0
target.qty = target_qty if flt(target_qty) >= 0 else 0
item = get_item_defaults(target.item_code, source_parent.company)
if item:
target.item_name = item.get("item_name")

View File

@ -1017,6 +1017,8 @@ def get_bom_item_rate(args, bom_doc):
item_doc = frappe.get_cached_doc("Item", args.get("item_code"))
price_list_data = get_price_list_rate(bom_args, item_doc)
rate = price_list_data.price_list_rate
elif bom_doc.rm_cost_as_per == "Manual":
return
return flt(rate)
@ -1381,7 +1383,7 @@ def get_bom_diff(bom1, bom2):
# check for deletions
for d in old_value:
if not d.get(identifier) in new_row_by_identifier:
if d.get(identifier) not in new_row_by_identifier:
out.removed.append([df.fieldname, d.as_dict()])
return out
@ -1397,13 +1399,18 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
fields = ["name", "item_name", "item_group", "description"]
fields.extend(
[field for field in searchfields if not field in ["name", "item_group", "description"]]
[field for field in searchfields if field not in ["name", "item_group", "description"]]
)
searchfields = searchfields + [
field
for field in [searchfield or "name", "item_code", "item_group", "item_name"]
if not field in searchfields
for field in [
searchfield or "name",
"item_code",
"item_group",
"item_name",
]
if field not in searchfields
]
query_filters = {"disabled": 0, "ifnull(end_of_life, '3099-12-31')": (">", today())}

View File

@ -583,6 +583,7 @@ class ProductionPlan(Document):
if close:
self.db_set("status", "Closed")
self.update_bin_qty()
return
if self.total_produced_qty > 0:
@ -597,6 +598,9 @@ class ProductionPlan(Document):
if close is not None:
self.db_set("status", self.status)
if self.docstatus == 1 and self.status != "Completed":
self.update_bin_qty()
def update_ordered_status(self):
update_status = False
for d in self.po_items:
@ -815,7 +819,7 @@ class ProductionPlan(Document):
key = "{}:{}:{}".format(item.sales_order, material_request_type, item_doc.customer or "")
schedule_date = item.schedule_date or add_days(nowdate(), cint(item_doc.lead_time_days))
if not key in material_request_map:
if key not in material_request_map:
# make a new MR for the combination
material_request_map[key] = frappe.new_doc("Material Request")
material_request = material_request_map[key]
@ -1597,19 +1601,23 @@ def get_materials_from_other_locations(item, warehouses, new_mr_items, company):
)
locations = get_available_item_locations(
item.get("item_code"), warehouses, item.get("quantity"), company, ignore_validation=True
item.get("item_code"),
warehouses,
item.get("quantity") * item.get("conversion_factor"),
company,
ignore_validation=True,
)
required_qty = item.get("quantity")
if item.get("conversion_factor") and item.get("purchase_uom") != item.get("stock_uom"):
# Convert qty to stock UOM
required_qty = required_qty * item.get("conversion_factor")
# get available material by transferring to production warehouse
for d in locations:
if required_qty <= 0:
return
conversion_factor = 1.0
if purchase_uom != stock_uom and purchase_uom == item["uom"]:
conversion_factor = get_uom_conversion_factor(item["item_code"], item["uom"])
new_dict = copy.deepcopy(item)
quantity = required_qty if d.get("qty") > required_qty else d.get("qty")
@ -1619,10 +1627,11 @@ def get_materials_from_other_locations(item, warehouses, new_mr_items, company):
"material_request_type": "Material Transfer",
"uom": new_dict.get("stock_uom"), # internal transfer should be in stock UOM
"from_warehouse": d.get("warehouse"),
"conversion_factor": 1.0,
}
)
required_qty -= quantity / conversion_factor
required_qty -= quantity
new_mr_items.append(new_dict)
# raise purchase request for remaining qty
@ -1634,7 +1643,7 @@ def get_materials_from_other_locations(item, warehouses, new_mr_items, company):
if frappe.db.get_value("UOM", purchase_uom, "must_be_whole_number"):
required_qty = ceil(required_qty)
item["quantity"] = required_qty
item["quantity"] = required_qty / item.get("conversion_factor")
new_mr_items.append(item)

View File

@ -1283,12 +1283,14 @@ class TestProductionPlan(FrappeTestCase):
for row in items:
row = frappe._dict(row)
if row.material_request_type == "Material Transfer":
self.assertTrue(row.uom == row.stock_uom)
self.assertTrue(row.from_warehouse in [wh1, wh2])
self.assertEqual(row.quantity, 2)
if row.material_request_type == "Purchase":
self.assertTrue(row.uom != row.stock_uom)
self.assertTrue(row.warehouse == mrp_warhouse)
self.assertEqual(row.quantity, 12)
self.assertEqual(row.quantity, 12.0)
def test_mr_qty_for_same_rm_with_different_sub_assemblies(self):
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
@ -1404,6 +1406,99 @@ class TestProductionPlan(FrappeTestCase):
self.assertEqual(after_qty, before_qty)
def test_material_request_qty_purchase_and_material_transfer(self):
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name
bom_item = make_item(
properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1", "purchase_uom": "Nos"}
).name
store_warehouse = create_warehouse("Store Warehouse", company="_Test Company")
rm_warehouse = create_warehouse("RM Warehouse", company="_Test Company")
make_stock_entry(
item_code=bom_item,
qty=60,
target=store_warehouse,
rate=99,
)
if not frappe.db.exists("UOM Conversion Detail", {"parent": bom_item, "uom": "Nos"}):
doc = frappe.get_doc("Item", bom_item)
doc.append("uoms", {"uom": "Nos", "conversion_factor": 10})
doc.save()
make_bom(item=fg_item, raw_materials=[bom_item], source_warehouse="_Test Warehouse - _TC")
pln = create_production_plan(
item_code=fg_item, planned_qty=10, stock_uom="_Test UOM 1", do_not_submit=1
)
pln.for_warehouse = rm_warehouse
items = get_items_for_material_requests(
pln.as_dict(), warehouses=[{"warehouse": store_warehouse}]
)
for row in items:
self.assertEqual(row.get("quantity"), 10.0)
self.assertEqual(row.get("material_request_type"), "Material Transfer")
self.assertEqual(row.get("uom"), "_Test UOM 1")
self.assertEqual(row.get("from_warehouse"), store_warehouse)
self.assertEqual(row.get("conversion_factor"), 1.0)
items = get_items_for_material_requests(
pln.as_dict(), warehouses=[{"warehouse": pln.for_warehouse}]
)
for row in items:
self.assertEqual(row.get("quantity"), 1.0)
self.assertEqual(row.get("material_request_type"), "Purchase")
self.assertEqual(row.get("uom"), "Nos")
self.assertEqual(row.get("conversion_factor"), 10.0)
def test_unreserve_qty_on_closing_of_pp(self):
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.utils import get_or_make_bin
fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name
rm_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name
store_warehouse = create_warehouse("Store Warehouse", company="_Test Company")
rm_warehouse = create_warehouse("RM Warehouse", company="_Test Company")
make_bom(item=fg_item, raw_materials=[rm_item], source_warehouse="_Test Warehouse - _TC")
pln = create_production_plan(
item_code=fg_item, planned_qty=10, stock_uom="_Test UOM 1", do_not_submit=1
)
pln.for_warehouse = rm_warehouse
mr_items = get_items_for_material_requests(pln.as_dict())
for d in mr_items:
pln.append("mr_items", d)
pln.save()
pln.submit()
bin_name = get_or_make_bin(rm_item, rm_warehouse)
before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
pln.reload()
pln.set_status(close=True)
bin_name = get_or_make_bin(rm_item, rm_warehouse)
after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
self.assertAlmostEqual(after_qty, before_qty - 10)
pln.reload()
pln.set_status(close=False)
bin_name = get_or_make_bin(rm_item, rm_warehouse)
after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
self.assertAlmostEqual(after_qty, before_qty)
def create_production_plan(**args):
"""

View File

@ -930,7 +930,7 @@ class WorkOrder(Document):
validate_end_of_life(self.production_item)
def validate_qty(self):
if not self.qty > 0:
if self.qty <= 0:
frappe.throw(_("Quantity to Manufacture must be greater than 0."))
if (
@ -957,7 +957,7 @@ class WorkOrder(Document):
max_qty = qty_dict.get("planned_qty", 0) + allowance_qty - qty_dict.get("ordered_qty", 0)
if not max_qty > 0:
if max_qty <= 0:
frappe.throw(
_("Cannot produce more item for {0}").format(self.production_item), OverProductionError
)
@ -968,7 +968,7 @@ class WorkOrder(Document):
)
def validate_transfer_against(self):
if not self.docstatus == 1:
if self.docstatus != 1:
# let user configure operations until they're ready to submit
return
if not self.operations:
@ -981,7 +981,7 @@ class WorkOrder(Document):
def validate_operation_time(self):
for d in self.operations:
if not d.time_in_mins > 0:
if d.time_in_mins <= 0:
frappe.throw(_("Operation Time must be greater than 0 for Operation {0}").format(d.operation))
def update_required_items(self):

View File

@ -351,6 +351,8 @@ erpnext.patches.v15_0.set_reserved_stock_in_bin
erpnext.patches.v14_0.create_accounting_dimensions_in_supplier_quotation
erpnext.patches.v14_0.update_zero_asset_quantity_field
execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction")
execute:frappe.db.set_default("date_format", frappe.db.get_single_value("System Settings", "date_format"))
erpnext.patches.v15_0.create_advance_payment_status
# below migration patch should always run last
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index

View File

@ -10,7 +10,7 @@ def execute():
)
if property_setter_name:
property_setter = frappe.get_doc("Property Setter", property_setter_name)
if not "Completed" in property_setter.value:
if "Completed" not in property_setter.value:
property_setter.value = property_setter.value + "\nCompleted"
property_setter.save()

View File

@ -46,7 +46,7 @@ def execute():
{"response_time": response_time, "resolution_time": resolution_time},
)
if priority.parenttype == "Service Level":
if not priority.parent in priority_dict:
if priority.parent not in priority_dict:
priority_dict[priority.parent] = []
priority_dict[priority.parent].append(priority)

View File

@ -3,6 +3,7 @@ import frappe
def execute():
frappe.reload_doc("assets", "doctype", "Asset Depreciation Schedule")
frappe.reload_doc("assets", "doctype", "Asset Finance Book")
assets = get_details_of_draft_or_submitted_depreciable_assets()
@ -86,6 +87,7 @@ def get_asset_finance_books_map():
afb.frequency_of_depreciation,
afb.rate_of_depreciation,
afb.expected_value_after_useful_life,
afb.daily_prorata_based,
afb.shift_based,
)
.where(asset.docstatus < 2)

View File

@ -2,14 +2,6 @@
// For license information, please see license.txt
frappe.ui.form.on('Homepage', {
setup: function(frm) {
frm.fields_dict["products"].grid.get_field("item").get_query = function() {
return {
filters: {'published': 1}
}
}
},
refresh: function(frm) {
frm.add_custom_button(__('Set Meta Tags'), () => {
frappe.utils.set_meta_tag('home');

View File

@ -50,7 +50,7 @@ def create_customer_or_supplier():
party = frappe.new_doc(doctype)
fullname = frappe.utils.get_fullname(user)
if not doctype == "Customer":
if doctype != "Customer":
party.update(
{
"supplier_name": fullname,

View File

@ -661,7 +661,7 @@ def set_project_status(project, status):
"""
set status for project and all related tasks
"""
if not status in ("Completed", "Cancelled"):
if status not in ("Completed", "Cancelled"):
frappe.throw(_("Status must be Cancelled or Completed"))
project = frappe.get_doc("Project", project)

View File

@ -36,14 +36,14 @@ erpnext.buying = {
// no idea where me is coming from
if(this.frm.get_field('shipping_address')) {
this.frm.set_query("shipping_address", function() {
if(me.frm.doc.customer) {
this.frm.set_query("shipping_address", () => {
if(this.frm.doc.customer) {
return {
query: 'frappe.contacts.doctype.address.address.address_query',
filters: { link_doctype: 'Customer', link_name: me.frm.doc.customer }
filters: { link_doctype: 'Customer', link_name: this.frm.doc.customer }
};
} else
return erpnext.queries.company_address_query(me.frm.doc)
return erpnext.queries.company_address_query(this.frm.doc)
});
}
}
@ -361,9 +361,14 @@ erpnext.buying = {
new erpnext.SerialBatchPackageSelector(
me.frm, item, (r) => {
if (r) {
let qty = Math.abs(r.total_qty);
if (doc.is_return) {
qty = qty * -1;
}
let update_values = {
"serial_and_batch_bundle": r.name,
"qty": Math.abs(r.total_qty)
"qty": qty
}
if (r.warehouse) {
@ -396,9 +401,14 @@ erpnext.buying = {
new erpnext.SerialBatchPackageSelector(
me.frm, item, (r) => {
if (r) {
let qty = Math.abs(r.total_qty);
if (doc.is_return) {
qty = qty * -1;
}
let update_values = {
"serial_and_batch_bundle": r.name,
"rejected_qty": Math.abs(r.total_qty)
"rejected_qty": qty
}
if (r.warehouse) {

View File

@ -380,6 +380,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}
scan_barcode() {
frappe.flags.dialog_set = false;
const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:this.frm});
barcode_scanner.process_scan();
}

View File

@ -2,10 +2,16 @@ frappe.provide("erpnext.financial_statements");
erpnext.financial_statements = {
"filters": get_filters(),
"formatter": function(value, row, column, data, default_formatter) {
"formatter": function(value, row, column, data, default_formatter, filter) {
if (data && column.fieldname=="account") {
value = data.account_name || value;
if (filter && filter?.text && filter?.type == "contains") {
if (!value.toLowerCase().includes(filter.text)) {
return value;
}
}
if (data.account) {
column.link_onclick =
"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";

View File

@ -8,7 +8,7 @@ $.extend(erpnext, {
if(!company && cur_frm)
company = cur_frm.doc.company;
if(company)
return frappe.get_doc(":Company", company).default_currency || frappe.boot.sysdefaults.currency;
return frappe.get_doc(":Company", company)?.default_currency || frappe.boot.sysdefaults.currency;
else
return frappe.boot.sysdefaults.currency;
},
@ -1077,7 +1077,7 @@ function set_time_to_resolve_and_response(frm, apply_sla_for_resolution) {
}
function get_time_left(timestamp, agreement_status) {
const diff = moment(timestamp).diff(moment());
const diff = moment(timestamp).diff(frappe.datetime.system_datetime(true));
const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : 'Failed';
let indicator = (diff_display == 'Failed' && agreement_status != 'Fulfilled') ? 'red' : 'green';
return {'diff_display': diff_display, 'indicator': indicator};

View File

@ -114,13 +114,13 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
frappe.run_serially([
() => this.set_selector_trigger_flag(data),
() => this.set_serial_no(row, serial_no),
() => this.set_batch_no(row, batch_no),
() => this.set_barcode(row, barcode),
() => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => {
this.show_scan_message(row.idx, row.item_code, qty);
}),
() => this.set_barcode_uom(row, uom),
() => this.set_serial_no(row, serial_no),
() => this.set_batch_no(row, batch_no),
() => this.set_barcode(row, barcode),
() => this.clean_up(),
() => this.revert_selector_flag(),
() => resolve(row)
@ -131,10 +131,10 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
// batch and serial selector is reduandant when all info can be added by scan
// this flag on item row is used by transaction.js to avoid triggering selector
set_selector_trigger_flag(data) {
const {batch_no, serial_no, has_batch_no, has_serial_no} = data;
const {has_batch_no, has_serial_no} = data;
const require_selecting_batch = has_batch_no && !batch_no;
const require_selecting_serial = has_serial_no && !serial_no;
const require_selecting_batch = has_batch_no;
const require_selecting_serial = has_serial_no;
if (!(require_selecting_batch || require_selecting_serial)) {
frappe.flags.hide_serial_batch_dialog = true;

View File

@ -317,9 +317,14 @@ erpnext.sales_common = {
new erpnext.SerialBatchPackageSelector(
me.frm, item, (r) => {
if (r) {
let qty = Math.abs(r.total_qty);
if (doc.is_return) {
qty = qty * -1;
}
frappe.model.set_value(item.doctype, item.name, {
"serial_and_batch_bundle": r.name,
"qty": Math.abs(r.total_qty)
"qty": qty
});
}
}

View File

@ -31,19 +31,40 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
secondary_action: () => this.edit_full_form(),
});
this.dialog.set_value("qty", this.item.qty).then(() => {
if (this.item.serial_no) {
this.dialog.set_value("scan_serial_no", this.item.serial_no);
frappe.model.set_value(this.item.doctype, this.item.name, 'serial_no', '');
} else if (this.item.batch_no) {
this.dialog.set_value("scan_batch_no", this.item.batch_no);
frappe.model.set_value(this.item.doctype, this.item.name, 'batch_no', '');
}
});
this.dialog.show();
this.$scan_btn = this.dialog.$wrapper.find(".link-btn");
this.$scan_btn.css("display", "inline");
let qty = this.item.stock_qty || this.item.transfer_qty || this.item.qty;
if (this.item?.is_rejected) {
qty = this.item.rejected_qty;
}
qty = Math.abs(qty);
if (qty > 0) {
this.dialog.set_value("qty", qty).then(() => {
if (this.item.serial_no && !this.item.serial_and_batch_bundle) {
let serial_nos = this.item.serial_no.split('\n');
if (serial_nos.length > 1) {
serial_nos.forEach(serial_no => {
this.dialog.fields_dict.entries.df.data.push({
serial_no: serial_no,
batch_no: this.item.batch_no
});
});
} else {
this.dialog.set_value("scan_serial_no", this.item.serial_no);
}
frappe.model.set_value(this.item.doctype, this.item.name, 'serial_no', '');
} else if (this.item.batch_no && !this.item.serial_and_batch_bundle) {
this.dialog.set_value("scan_batch_no", this.item.batch_no);
frappe.model.set_value(this.item.doctype, this.item.name, 'batch_no', '');
}
this.dialog.fields_dict.entries.grid.refresh();
});
}
}
get_serial_no_filters() {
@ -463,13 +484,13 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate {
}
render_data() {
if (!this.frm.is_new() && this.bundle) {
if (this.bundle) {
frappe.call({
method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.get_serial_batch_ledgers',
args: {
item_code: this.item.item_code,
name: this.bundle,
voucher_no: this.item.parent,
voucher_no: !this.frm.is_new() ? this.item.parent : "",
}
}).then(r => {
if (r.message) {

View File

@ -153,7 +153,9 @@ def make_gl_entry(tax, gl_entries, doc, tax_accounts):
"account": tax.account_head,
"cost_center": tax.cost_center,
"posting_date": doc.posting_date,
"against_type": "Supplier",
"against": doc.supplier,
"against_link": doc.supplier,
dr_or_cr: tax.base_tax_amount_after_discount_amount,
dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount
if account_currency == doc.company_currency

View File

@ -240,7 +240,7 @@ def get_chart_data(data):
for row in data:
item_key = row.get("item_code")
if not item_key in item_wise_sales_map:
if item_key not in item_wise_sales_map:
item_wise_sales_map[item_key] = 0
item_wise_sales_map[item_key] = flt(item_wise_sales_map[item_key]) + flt(row.get("amount"))

View File

@ -167,7 +167,7 @@ def prepare_data(data, so_elapsed_time, filters):
if filters.get("group_by_so"):
so_name = row["sales_order"]
if not so_name in sales_order_map:
if so_name not in sales_order_map:
# create an entry
row_copy = copy.deepcopy(row)
sales_order_map[so_name] = row_copy

View File

@ -112,9 +112,9 @@ def create_transaction(doctype, company, start_date):
warehouse = get_warehouse(company)
if document_type == "Purchase Order":
posting_date = get_random_date(start_date, 1, 30)
posting_date = get_random_date(start_date, 1, 25)
else:
posting_date = get_random_date(start_date, 31, 364)
posting_date = get_random_date(start_date, 31, 350)
doctype.update(
{

View File

@ -185,7 +185,10 @@ class AuthorizationControl(TransactionBase):
# Remove user specific rules from global authorization rules
for r in based_on:
if r in final_based_on and not r in ["Itemwise Discount", "Item Group wise Discount"]:
if r in final_based_on and r not in [
"Itemwise Discount",
"Item Group wise Discount",
]:
final_based_on.remove(r)
# Check for authorization set on particular roles
@ -213,7 +216,10 @@ class AuthorizationControl(TransactionBase):
# Remove role specific rules from global authorization rules
for r in based_on:
if r in final_based_on and not r in ["Itemwise Discount", "Item Group wise Discount"]:
if r in final_based_on and r not in [
"Itemwise Discount",
"Item Group wise Discount",
]:
final_based_on.remove(r)
# Check for global authorization

View File

@ -1,21 +1,6 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.set_root_readonly(doc);
}
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root customer group
if(!doc.parent_customer_group && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root customer group and cannot be edited."));
} else {
cur_frm.set_intro(null);
}
}
frappe.ui.form.on("Customer Group", {
setup: function(frm){
frm.set_query('parent_customer_group', function (doc) {
@ -48,5 +33,17 @@ frappe.ui.form.on("Customer Group", {
}
}
});
}
},
refresh: function(frm) {
frm.trigger("set_root_readonly");
},
set_root_readonly: function(frm) {
// read-only for root customer group
if(!frm.doc.parent_customer_group && !frm.doc.__islocal) {
frm.set_read_only();
frm.set_intro(__("This is a root customer group and cannot be edited."));
} else {
frm.set_intro(null);
}
},
});

View File

@ -44,7 +44,7 @@ class Department(NestedSet):
def before_rename(self, old, new, merge=False):
# renaming consistency with abbreviation
if not frappe.get_cached_value("Company", self.company, "abbr") in new:
if frappe.get_cached_value("Company", self.company, "abbr") not in new:
new = get_abbreviated_name(new, self.company)
return new

View File

@ -689,7 +689,7 @@ class EmailDigest(Document):
]
def get_root_type_accounts(self, root_type):
if not root_type in self._accounts:
if root_type not in self._accounts:
self._accounts[root_type] = [
d.name
for d in frappe.db.get_all(

View File

@ -187,7 +187,7 @@ class Employee(NestedSet):
throw(_("Please enter relieving date."))
def validate_for_enabled_user_id(self, enabled):
if not self.status == "Active":
if self.status != "Active":
return
if enabled is None:

View File

@ -11,6 +11,7 @@ frappe.ui.form.on('Sales Person', {
frm.dashboard.add_indicator(__('Total Contribution Amount Against Invoices: {0}',
[format_currency(info.allocated_amount_against_invoice, info.currency)]), 'blue');
}
frm.trigger("set_root_readonly");
},
setup: function(frm) {
@ -27,22 +28,18 @@ frappe.ui.form.on('Sales Person', {
'Sales Order': () => frappe.new_doc("Sales Order")
.then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name}))
}
},
set_root_readonly: function(frm) {
// read-only for root
if(!frm.doc.parent_sales_person && !frm.doc.__islocal) {
frm.set_read_only();
frm.set_intro(__("This is a root sales person and cannot be edited."));
} else {
frm.set_intro(null);
}
}
});
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.set_root_readonly(doc);
}
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root
if(!doc.parent_sales_person && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root sales person and cannot be edited."));
} else {
cur_frm.set_intro(null);
}
}
//get query select sales person
cur_frm.fields_dict['parent_sales_person'].get_query = function(doc, cdt, cdn) {

View File

@ -1,21 +1,6 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
cur_frm.cscript.refresh = function(doc) {
cur_frm.set_intro(doc.__islocal ? "" : __("There is nothing to edit."));
cur_frm.cscript.set_root_readonly(doc);
};
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root customer group
if(!doc.parent_supplier_group && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root supplier group and cannot be edited."));
} else {
cur_frm.set_intro(null);
}
};
frappe.ui.form.on("Supplier Group", {
setup: function(frm){
frm.set_query('parent_supplier_group', function (doc) {
@ -48,5 +33,17 @@ frappe.ui.form.on("Supplier Group", {
}
}
});
},
refresh: function(frm) {
frm.set_intro(frm.doc.__islocal ? "" : __("There is nothing to edit."));
frm.trigger("set_root_readonly");
},
set_root_readonly: function(frm) {
if(!frm.doc.parent_supplier_group && !frm.doc.__islocal) {
frm.trigger("set_read_only");
frm.set_intro(__("This is a root supplier group and cannot be edited."));
} else {
frm.set_intro(null);
}
}
});

View File

@ -11,23 +11,22 @@ frappe.ui.form.on("Territory", {
}
}
};
},
refresh: function(frm) {
frm.trigger("set_root_readonly");
},
set_root_readonly: function(frm) {
// read-only for root territory
if(!frm.doc.parent_territory && !frm.doc.__islocal) {
frm.set_read_only();
frm.set_intro(__("This is a root territory and cannot be edited."));
} else {
frm.set_intro(null);
}
}
});
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.set_root_readonly(doc);
}
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root territory
if(!doc.parent_territory && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root territory and cannot be edited."));
} else {
cur_frm.set_intro(null);
}
}
//get query select territory
cur_frm.fields_dict['parent_territory'].get_query = function(doc,cdt,cdn) {
return{

View File

@ -85,8 +85,6 @@ def set_single_defaults():
except frappe.ValidationError:
pass
frappe.db.set_default("date_format", "dd-mm-yyyy")
setup_currency_exchange()
@ -197,7 +195,7 @@ def add_standard_navbar_items():
for item in erpnext_navbar_items:
current_labels = [item.get("item_label") for item in current_navbar_items]
if not item.get("item_label") in current_labels:
if item.get("item_label") not in current_labels:
navbar_settings.append("help_dropdown", item)
for item in current_navbar_items:

Some files were not shown because too many files have changed in this diff Show More