mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-25 17:48:30 +00:00
Merge pull request #34161 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
# the repo. Unless a later match takes precedence,
|
||||
|
||||
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/assets/ @anandbaburajan @deepeshgarg007
|
||||
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
|
||||
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||
@@ -16,6 +16,7 @@ erpnext/maintenance/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/stock/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/subcontracting @rohitwaghchaure @s-aga-r
|
||||
|
||||
erpnext/crm/ @NagariaHussain
|
||||
erpnext/education/ @rutwikhdev
|
||||
|
||||
@@ -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'];
|
||||
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger"];
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
|
||||
@@ -89,7 +89,13 @@ class JournalEntry(AccountsController):
|
||||
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
||||
|
||||
unlink_ref_doc_from_payment_entries(self)
|
||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||
self.ignore_linked_doctypes = (
|
||||
"GL Entry",
|
||||
"Stock Ledger Entry",
|
||||
"Payment Ledger Entry",
|
||||
"Repost Payment Ledger",
|
||||
"Repost Payment Ledger Items",
|
||||
)
|
||||
self.make_gl_entries(1)
|
||||
self.update_advance_paid()
|
||||
self.unlink_advance_entry_reference()
|
||||
@@ -238,21 +244,16 @@ class JournalEntry(AccountsController):
|
||||
):
|
||||
processed_assets.append(d.reference_name)
|
||||
|
||||
asset = frappe.db.get_value(
|
||||
"Asset", d.reference_name, ["calculate_depreciation", "value_after_depreciation"], as_dict=1
|
||||
)
|
||||
asset = frappe.get_doc("Asset", d.reference_name)
|
||||
|
||||
if asset.calculate_depreciation:
|
||||
continue
|
||||
|
||||
depr_value = d.debit or d.credit
|
||||
|
||||
frappe.db.set_value(
|
||||
"Asset",
|
||||
d.reference_name,
|
||||
"value_after_depreciation",
|
||||
asset.value_after_depreciation - depr_value,
|
||||
)
|
||||
asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value)
|
||||
|
||||
asset.set_status()
|
||||
|
||||
def update_inter_company_jv(self):
|
||||
if (
|
||||
@@ -341,12 +342,9 @@ class JournalEntry(AccountsController):
|
||||
else:
|
||||
depr_value = d.debit or d.credit
|
||||
|
||||
frappe.db.set_value(
|
||||
"Asset",
|
||||
d.reference_name,
|
||||
"value_after_depreciation",
|
||||
asset.value_after_depreciation + depr_value,
|
||||
)
|
||||
asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value)
|
||||
|
||||
asset.set_status()
|
||||
|
||||
def unlink_inter_company_jv(self):
|
||||
if (
|
||||
|
||||
@@ -7,7 +7,7 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges";
|
||||
|
||||
frappe.ui.form.on('Payment Entry', {
|
||||
onload: function(frm) {
|
||||
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice'];
|
||||
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', "Repost Payment Ledger"];
|
||||
|
||||
if(frm.doc.__islocal) {
|
||||
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
|
||||
|
||||
@@ -239,7 +239,7 @@
|
||||
"depends_on": "paid_from",
|
||||
"fieldname": "paid_from_account_currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account Currency",
|
||||
"label": "Account Currency (From)",
|
||||
"options": "Currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
@@ -249,7 +249,7 @@
|
||||
"depends_on": "paid_from",
|
||||
"fieldname": "paid_from_account_balance",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Account Balance",
|
||||
"label": "Account Balance (From)",
|
||||
"options": "paid_from_account_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -272,7 +272,7 @@
|
||||
"depends_on": "paid_to",
|
||||
"fieldname": "paid_to_account_currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account Currency",
|
||||
"label": "Account Currency (To)",
|
||||
"options": "Currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
@@ -282,7 +282,7 @@
|
||||
"depends_on": "paid_to",
|
||||
"fieldname": "paid_to_account_balance",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Account Balance",
|
||||
"label": "Account Balance (To)",
|
||||
"options": "paid_to_account_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -304,7 +304,7 @@
|
||||
{
|
||||
"fieldname": "source_exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "Exchange Rate",
|
||||
"label": "Source Exchange Rate",
|
||||
"precision": "9",
|
||||
"print_hide": 1,
|
||||
"reqd": 1
|
||||
@@ -334,7 +334,7 @@
|
||||
{
|
||||
"fieldname": "target_exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "Exchange Rate",
|
||||
"label": "Target Exchange Rate",
|
||||
"precision": "9",
|
||||
"print_hide": 1,
|
||||
"reqd": 1
|
||||
@@ -633,14 +633,14 @@
|
||||
"depends_on": "eval:doc.party_type == 'Supplier'",
|
||||
"fieldname": "purchase_taxes_and_charges_template",
|
||||
"fieldtype": "Link",
|
||||
"label": "Taxes and Charges Template",
|
||||
"label": "Purchase Taxes and Charges Template",
|
||||
"options": "Purchase Taxes and Charges Template"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.party_type == 'Customer'",
|
||||
"fieldname": "sales_taxes_and_charges_template",
|
||||
"fieldtype": "Link",
|
||||
"label": "Taxes and Charges Template",
|
||||
"label": "Sales Taxes and Charges Template",
|
||||
"options": "Sales Taxes and Charges Template"
|
||||
},
|
||||
{
|
||||
@@ -733,7 +733,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-12-08 16:25:43.824051",
|
||||
"modified": "2023-02-14 04:52:30.478523",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
|
||||
@@ -92,7 +92,13 @@ class PaymentEntry(AccountsController):
|
||||
self.set_status()
|
||||
|
||||
def on_cancel(self):
|
||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||
self.ignore_linked_doctypes = (
|
||||
"GL Entry",
|
||||
"Stock Ledger Entry",
|
||||
"Payment Ledger Entry",
|
||||
"Repost Payment Ledger",
|
||||
"Repost Payment Ledger Items",
|
||||
)
|
||||
self.make_gl_entries(cancel=1)
|
||||
self.update_outstanding_amounts()
|
||||
self.update_advance_paid()
|
||||
|
||||
@@ -368,6 +368,7 @@ class PaymentReconciliation(Document):
|
||||
"exchange_rate": 1,
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
reverse_dr_or_cr + "_in_account_currency": flt(row.difference_amount),
|
||||
reverse_dr_or_cr: flt(row.difference_amount),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -495,7 +495,7 @@ def get_amount(ref_doc, payment_account=None):
|
||||
"""get amount based on doctype"""
|
||||
dt = ref_doc.doctype
|
||||
if dt in ["Sales Order", "Purchase Order"]:
|
||||
grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
|
||||
grand_total = flt(ref_doc.rounded_total) - flt(ref_doc.advance_paid)
|
||||
|
||||
elif dt in ["Sales Invoice", "Purchase Invoice"]:
|
||||
if ref_doc.party_account_currency == ref_doc.currency:
|
||||
|
||||
@@ -21,8 +21,24 @@ class POSClosingEntry(StatusUpdater):
|
||||
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
|
||||
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
|
||||
|
||||
self.validate_duplicate_pos_invoices()
|
||||
self.validate_pos_invoices()
|
||||
|
||||
def validate_duplicate_pos_invoices(self):
|
||||
pos_occurences = {}
|
||||
for idx, inv in enumerate(self.pos_transactions, 1):
|
||||
pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
|
||||
|
||||
error_list = []
|
||||
for key, value in pos_occurences.items():
|
||||
if len(value) > 1:
|
||||
error_list.append(
|
||||
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
|
||||
)
|
||||
|
||||
if error_list:
|
||||
frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
|
||||
|
||||
def validate_pos_invoices(self):
|
||||
invalid_rows = []
|
||||
for d in self.pos_transactions:
|
||||
|
||||
@@ -18,6 +18,22 @@ class POSInvoiceMergeLog(Document):
|
||||
def validate(self):
|
||||
self.validate_customer()
|
||||
self.validate_pos_invoice_status()
|
||||
self.validate_duplicate_pos_invoices()
|
||||
|
||||
def validate_duplicate_pos_invoices(self):
|
||||
pos_occurences = {}
|
||||
for idx, inv in enumerate(self.pos_invoices, 1):
|
||||
pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
|
||||
|
||||
error_list = []
|
||||
for key, value in pos_occurences.items():
|
||||
if len(value) > 1:
|
||||
error_list.append(
|
||||
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
|
||||
)
|
||||
|
||||
if error_list:
|
||||
frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
|
||||
|
||||
def validate_customer(self):
|
||||
if self.merge_invoices_based_on == "Customer Group":
|
||||
@@ -426,6 +442,8 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
|
||||
|
||||
if closing_entry:
|
||||
closing_entry.set_status(update=True, status="Failed")
|
||||
if type(error_message) == list:
|
||||
error_message = frappe.json.dumps(error_message)
|
||||
closing_entry.db_set("error_message", error_message)
|
||||
raise
|
||||
|
||||
|
||||
@@ -472,7 +472,7 @@
|
||||
"description": "If rate is zero them item will be treated as \"Free Item\"",
|
||||
"fieldname": "free_item_rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rate"
|
||||
"label": "Free Item Rate"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
@@ -608,7 +608,7 @@
|
||||
"icon": "fa fa-gift",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2022-10-13 19:05:35.056304",
|
||||
"modified": "2023-02-14 04:53:34.887358",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Pricing Rule",
|
||||
|
||||
@@ -31,7 +31,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'];
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger"];
|
||||
|
||||
if(!this.frm.doc.__islocal) {
|
||||
// show credit_to in print format
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import frappe
|
||||
from frappe import _, throw
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
|
||||
|
||||
import erpnext
|
||||
@@ -1416,6 +1417,8 @@ class PurchaseInvoice(BuyingController):
|
||||
"GL Entry",
|
||||
"Stock Ledger Entry",
|
||||
"Repost Item Valuation",
|
||||
"Repost Payment Ledger",
|
||||
"Repost Payment Ledger Items",
|
||||
"Payment Ledger Entry",
|
||||
"Tax Withheld Vouchers",
|
||||
)
|
||||
@@ -1463,19 +1466,16 @@ class PurchaseInvoice(BuyingController):
|
||||
def update_billing_status_in_pr(self, update_modified=True):
|
||||
updated_pr = []
|
||||
po_details = []
|
||||
|
||||
pr_details_billed_amt = self.get_pr_details_billed_amt()
|
||||
|
||||
for d in self.get("items"):
|
||||
if d.pr_detail:
|
||||
billed_amt = frappe.db.sql(
|
||||
"""select sum(amount) from `tabPurchase Invoice Item`
|
||||
where pr_detail=%s and docstatus=1""",
|
||||
d.pr_detail,
|
||||
)
|
||||
billed_amt = billed_amt and billed_amt[0][0] or 0
|
||||
frappe.db.set_value(
|
||||
"Purchase Receipt Item",
|
||||
d.pr_detail,
|
||||
"billed_amt",
|
||||
billed_amt,
|
||||
flt(pr_details_billed_amt.get(d.pr_detail)),
|
||||
update_modified=update_modified,
|
||||
)
|
||||
updated_pr.append(d.purchase_receipt)
|
||||
@@ -1491,6 +1491,24 @@ class PurchaseInvoice(BuyingController):
|
||||
pr_doc = frappe.get_doc("Purchase Receipt", pr)
|
||||
update_billing_percentage(pr_doc, update_modified=update_modified)
|
||||
|
||||
def get_pr_details_billed_amt(self):
|
||||
# Get billed amount based on purchase receipt item reference (pr_detail) in purchase invoice
|
||||
|
||||
pr_details_billed_amt = {}
|
||||
pr_details = [d.get("pr_detail") for d in self.get("items") if d.get("pr_detail")]
|
||||
if pr_details:
|
||||
doctype = frappe.qb.DocType("Purchase Invoice Item")
|
||||
query = (
|
||||
frappe.qb.from_(doctype)
|
||||
.select(doctype.pr_detail, Sum(doctype.amount))
|
||||
.where(doctype.pr_detail.isin(pr_details) & doctype.docstatus == 1)
|
||||
.groupby(doctype.pr_detail)
|
||||
)
|
||||
|
||||
pr_details_billed_amt = frappe._dict(query.run(as_list=1))
|
||||
|
||||
return pr_details_billed_amt
|
||||
|
||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||
self.due_date = None
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
||||
super.onload();
|
||||
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log',
|
||||
'POS Closing Entry', 'Journal Entry', 'Payment Entry'];
|
||||
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger"];
|
||||
|
||||
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
|
||||
// show debit_to in print format
|
||||
@@ -64,6 +64,25 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
||||
|
||||
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
|
||||
|
||||
if (this.frm.doc.repost_required && this.frm.doc.docstatus===1) {
|
||||
this.frm.set_intro(__("Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update."));
|
||||
this.frm.add_custom_button(__('Repost Accounting Entries'),
|
||||
() => {
|
||||
this.frm.call({
|
||||
doc: this.frm.doc,
|
||||
method: 'repost_accounting_entries',
|
||||
freeze: true,
|
||||
freeze_message: __('Reposting...'),
|
||||
callback: (r) => {
|
||||
if (!r.exc) {
|
||||
frappe.msgprint(__('Accounting Entries are reposted'));
|
||||
me.frm.refresh();
|
||||
}
|
||||
}
|
||||
});
|
||||
}).removeClass('btn-default').addClass('btn-warning');
|
||||
}
|
||||
|
||||
if (this.frm.doc.is_return) {
|
||||
this.frm.return_print_format = "Sales Invoice Return";
|
||||
}
|
||||
|
||||
@@ -209,6 +209,7 @@
|
||||
"is_internal_customer",
|
||||
"is_discounted",
|
||||
"remarks",
|
||||
"repost_required",
|
||||
"connections_tab"
|
||||
],
|
||||
"fields": [
|
||||
@@ -1038,6 +1039,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "redeem_loyalty_points",
|
||||
"fieldname": "loyalty_redemption_account",
|
||||
"fieldtype": "Link",
|
||||
@@ -1336,6 +1338,7 @@
|
||||
"options": "fa fa-money"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "is_pos",
|
||||
"fieldname": "cash_bank_account",
|
||||
"fieldtype": "Link",
|
||||
@@ -1435,6 +1438,7 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "is_pos",
|
||||
"fieldname": "account_for_change_amount",
|
||||
"fieldtype": "Link",
|
||||
@@ -1483,6 +1487,7 @@
|
||||
"hide_seconds": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "write_off_account",
|
||||
"fieldtype": "Link",
|
||||
"hide_days": 1,
|
||||
@@ -1706,6 +1711,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"default": "No",
|
||||
"fieldname": "is_opening",
|
||||
"fieldtype": "Select",
|
||||
@@ -1922,6 +1928,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_internal_customer",
|
||||
"description": "Unrealized Profit / Loss account for intra-company transfers",
|
||||
"fieldname": "unrealized_profit_loss_account",
|
||||
@@ -1964,6 +1971,7 @@
|
||||
"label": "Disable Rounded Total"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "additional_discount_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Discount Account",
|
||||
@@ -2114,6 +2122,15 @@
|
||||
"fieldname": "named_place",
|
||||
"fieldtype": "Data",
|
||||
"label": "Named Place"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "repost_required",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Repost Required",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
@@ -2126,7 +2143,7 @@
|
||||
"link_fieldname": "consolidated_invoice"
|
||||
}
|
||||
],
|
||||
"modified": "2023-01-28 19:45:47.538163",
|
||||
"modified": "2022-11-07 16:02:07.972258",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -11,6 +11,9 @@ from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_accounting_dimensions,
|
||||
)
|
||||
from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
|
||||
get_loyalty_program_details_with_points,
|
||||
validate_loyalty_points,
|
||||
@@ -100,13 +103,11 @@ class SalesInvoice(SellingController):
|
||||
self.validate_debit_to_acc()
|
||||
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
|
||||
self.add_remarks()
|
||||
self.validate_write_off_account()
|
||||
self.validate_account_for_change_amount()
|
||||
self.validate_fixed_asset()
|
||||
self.set_income_account_for_fixed_assets()
|
||||
self.validate_item_cost_centers()
|
||||
self.validate_income_account()
|
||||
self.check_conversion_rate()
|
||||
self.validate_accounts()
|
||||
|
||||
validate_inter_company_party(
|
||||
self.doctype, self.customer, self.company, self.inter_company_invoice_reference
|
||||
@@ -170,6 +171,11 @@ class SalesInvoice(SellingController):
|
||||
|
||||
self.reset_default_field_value("set_warehouse", "items", "warehouse")
|
||||
|
||||
def validate_accounts(self):
|
||||
self.validate_write_off_account()
|
||||
self.validate_account_for_change_amount()
|
||||
self.validate_income_account()
|
||||
|
||||
def validate_fixed_asset(self):
|
||||
for d in self.get("items"):
|
||||
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
|
||||
@@ -368,6 +374,7 @@ class SalesInvoice(SellingController):
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
self.db_set("status", "Cancelled")
|
||||
self.db_set("repost_required", 0)
|
||||
|
||||
if (
|
||||
frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
|
||||
@@ -390,6 +397,8 @@ class SalesInvoice(SellingController):
|
||||
"GL Entry",
|
||||
"Stock Ledger Entry",
|
||||
"Repost Item Valuation",
|
||||
"Repost Payment Ledger",
|
||||
"Repost Payment Ledger Items",
|
||||
"Payment Ledger Entry",
|
||||
)
|
||||
|
||||
@@ -514,6 +523,92 @@ class SalesInvoice(SellingController):
|
||||
def on_update(self):
|
||||
self.set_paid_amount()
|
||||
|
||||
def on_update_after_submit(self):
|
||||
if hasattr(self, "repost_required"):
|
||||
needs_repost = 0
|
||||
|
||||
# Check if any field affecting accounting entry is altered
|
||||
doc_before_update = self.get_doc_before_save()
|
||||
accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"]
|
||||
|
||||
# Check if opening entry check updated
|
||||
if doc_before_update.get("is_opening") != self.is_opening:
|
||||
needs_repost = 1
|
||||
|
||||
if not needs_repost:
|
||||
# Parent Level Accounts excluding party account
|
||||
for field in (
|
||||
"additional_discount_account",
|
||||
"cash_bank_account",
|
||||
"account_for_change_amount",
|
||||
"write_off_account",
|
||||
"loyalty_redemption_account",
|
||||
"unrealized_profit_loss_account",
|
||||
):
|
||||
if doc_before_update.get(field) != self.get(field):
|
||||
needs_repost = 1
|
||||
break
|
||||
|
||||
# Check for parent accounting dimensions
|
||||
for dimension in accounting_dimensions:
|
||||
if doc_before_update.get(dimension) != self.get(dimension):
|
||||
needs_repost = 1
|
||||
break
|
||||
|
||||
# Check for child tables
|
||||
if self.check_if_child_table_updated(
|
||||
"items",
|
||||
doc_before_update,
|
||||
("income_account", "expense_account", "discount_account"),
|
||||
accounting_dimensions,
|
||||
):
|
||||
needs_repost = 1
|
||||
|
||||
if self.check_if_child_table_updated(
|
||||
"taxes", doc_before_update, ("account_head",), accounting_dimensions
|
||||
):
|
||||
needs_repost = 1
|
||||
|
||||
self.validate_accounts()
|
||||
|
||||
# validate if deferred revenue is enabled for any item
|
||||
# Don't allow to update the invoice if deferred revenue is enabled
|
||||
for item in self.get("items"):
|
||||
if item.enable_deferred_revenue:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Deferred Revenue is enabled for item {0}. You cannot update the invoice after submission."
|
||||
).format(item.item_code)
|
||||
)
|
||||
|
||||
self.db_set("repost_required", needs_repost)
|
||||
|
||||
def check_if_child_table_updated(
|
||||
self, child_table, doc_before_update, fields_to_check, accounting_dimensions
|
||||
):
|
||||
# Check if any field affecting accounting entry is altered
|
||||
for index, item in enumerate(self.get(child_table)):
|
||||
for field in fields_to_check:
|
||||
if doc_before_update.get(child_table)[index].get(field) != item.get(field):
|
||||
return True
|
||||
|
||||
for dimension in accounting_dimensions:
|
||||
if doc_before_update.get(child_table)[index].get(dimension) != item.get(dimension):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@frappe.whitelist()
|
||||
def repost_accounting_entries(self):
|
||||
if self.repost_required:
|
||||
self.docstatus = 2
|
||||
self.make_gl_entries_on_cancel()
|
||||
self.docstatus = 1
|
||||
self.make_gl_entries()
|
||||
self.db_set("repost_required", 0)
|
||||
else:
|
||||
frappe.throw(_("No updates pending for reposting"))
|
||||
|
||||
def set_paid_amount(self):
|
||||
paid_amount = 0.0
|
||||
base_paid_amount = 0.0
|
||||
|
||||
@@ -2769,6 +2769,31 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
|
||||
|
||||
# Update Invoice post submit and then check GL Entries again
|
||||
|
||||
si.load_from_db()
|
||||
si.items[0].income_account = "Service - _TC"
|
||||
si.additional_discount_account = "_Test Account Sales - _TC"
|
||||
si.taxes[0].account_head = "TDS Payable - _TC"
|
||||
si.save()
|
||||
|
||||
si.load_from_db()
|
||||
self.assertTrue(si.repost_required)
|
||||
|
||||
si.repost_accounting_entries()
|
||||
|
||||
expected_gle = [
|
||||
["_Test Account Sales - _TC", 22.0, 0.0, nowdate()],
|
||||
["Debtors - _TC", 88, 0.0, nowdate()],
|
||||
["Service - _TC", 0.0, 100.0, nowdate()],
|
||||
["TDS Payable - _TC", 0.0, 10.0, nowdate()],
|
||||
]
|
||||
|
||||
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
|
||||
|
||||
si.load_from_db()
|
||||
self.assertFalse(si.repost_required)
|
||||
|
||||
def test_asset_depreciation_on_sale_with_pro_rata(self):
|
||||
"""
|
||||
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
|
||||
@@ -3326,6 +3351,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
|
||||
"""select account, debit, credit, posting_date
|
||||
from `tabGL Entry`
|
||||
where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s
|
||||
and is_cancelled = 0
|
||||
order by posting_date asc, account asc""",
|
||||
(voucher_no, posting_date),
|
||||
as_dict=1,
|
||||
|
||||
@@ -438,6 +438,7 @@
|
||||
"label": "Accounting Details"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "income_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Income Account",
|
||||
@@ -450,6 +451,7 @@
|
||||
"width": "120px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "expense_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Expense Account",
|
||||
@@ -469,6 +471,7 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"default": ":Company",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
@@ -800,6 +803,7 @@
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
@@ -822,7 +826,6 @@
|
||||
"label": "Incoming Rate (Costing)",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"precision": "6",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
@@ -835,6 +838,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "discount_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Discount Account",
|
||||
@@ -886,7 +890,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-12-28 16:17:33.484531",
|
||||
"modified": "2022-10-17 12:51:44.825398",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"columns": 2,
|
||||
"fieldname": "account_head",
|
||||
"fieldtype": "Link",
|
||||
@@ -63,6 +64,7 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"default": ":Company",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
@@ -216,12 +218,13 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-05 20:04:01.726867",
|
||||
"modified": "2022-10-17 13:08:17.776528",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC"
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
}
|
||||
@@ -278,7 +278,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
||||
tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers)
|
||||
|
||||
if cint(tax_details.round_off_tax_amount):
|
||||
tax_amount = round(tax_amount)
|
||||
tax_amount = normal_round(tax_amount)
|
||||
|
||||
return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount
|
||||
|
||||
@@ -603,3 +603,20 @@ def is_valid_certificate(
|
||||
valid = True
|
||||
|
||||
return valid
|
||||
|
||||
|
||||
def normal_round(number):
|
||||
"""
|
||||
Rounds a number to the nearest integer.
|
||||
:param number: The number to round.
|
||||
"""
|
||||
decimal_part = number - int(number)
|
||||
|
||||
if decimal_part >= 0.5:
|
||||
decimal_part = 1
|
||||
else:
|
||||
decimal_part = 0
|
||||
|
||||
number = int(number) + decimal_part
|
||||
|
||||
return number
|
||||
|
||||
@@ -250,7 +250,7 @@ frappe.ui.form.on('Asset', {
|
||||
$.each(depr_entries || [], function(i, v) {
|
||||
x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' }));
|
||||
let last_asset_value = asset_values[asset_values.length - 1]
|
||||
asset_values.push(last_asset_value - v.value);
|
||||
asset_values.push(flt(last_asset_value - v.value, precision('gross_purchase_amount')));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -677,11 +677,15 @@ class Asset(AccountsController):
|
||||
|
||||
if self.journal_entry_for_scrap:
|
||||
status = "Scrapped"
|
||||
elif self.finance_books:
|
||||
idx = self.get_default_finance_book_idx() or 0
|
||||
else:
|
||||
expected_value_after_useful_life = 0
|
||||
value_after_depreciation = self.value_after_depreciation
|
||||
|
||||
expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life
|
||||
value_after_depreciation = self.finance_books[idx].value_after_depreciation
|
||||
if self.calculate_depreciation:
|
||||
idx = self.get_default_finance_book_idx() or 0
|
||||
|
||||
expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life
|
||||
value_after_depreciation = self.finance_books[idx].value_after_depreciation
|
||||
|
||||
if flt(value_after_depreciation) <= expected_value_after_useful_life:
|
||||
status = "Fully Depreciated"
|
||||
@@ -843,6 +847,7 @@ class Asset(AccountsController):
|
||||
.where(gle.debit != 0)
|
||||
.where(gle.is_cancelled == 0)
|
||||
.orderby(gle.posting_date)
|
||||
.orderby(gle.creation)
|
||||
).run(as_dict=True)
|
||||
|
||||
return records
|
||||
|
||||
@@ -137,7 +137,7 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
finance_books.value_after_depreciation -= d.depreciation_amount
|
||||
finance_books.db_update()
|
||||
|
||||
frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Successful")
|
||||
asset.db_set("depr_entry_posting_status", "Successful")
|
||||
|
||||
asset.set_status()
|
||||
|
||||
|
||||
@@ -77,6 +77,9 @@ class AssetRepair(AccountsController):
|
||||
self.asset_doc.prepare_depreciation_data()
|
||||
self.asset_doc.save()
|
||||
|
||||
def after_delete(self):
|
||||
frappe.get_doc("Asset", self.asset).set_status()
|
||||
|
||||
def check_repair_status(self):
|
||||
if self.repair_status == "Pending":
|
||||
frappe.throw(_("Please update Repair Status."))
|
||||
|
||||
@@ -151,6 +151,7 @@ def prepare_chart_data(data, filters):
|
||||
filters.filter_based_on,
|
||||
"Monthly",
|
||||
company=filters.company,
|
||||
ignore_fiscal_year=True,
|
||||
)
|
||||
|
||||
for d in period_list:
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"allow_multiple_items",
|
||||
"bill_for_rejected_quantity_in_purchase_invoice",
|
||||
"disable_last_purchase_rate",
|
||||
"show_pay_button",
|
||||
"subcontract",
|
||||
"backflush_raw_materials_of_subcontract_based_on",
|
||||
"column_break_11",
|
||||
@@ -140,6 +141,12 @@
|
||||
"fieldname": "disable_last_purchase_rate",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Last Purchase Rate"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "show_pay_button",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Pay Button in Purchase Order Portal"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cog",
|
||||
@@ -147,7 +154,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2023-01-09 17:08:28.828173",
|
||||
"modified": "2023-02-15 14:42:10.200679",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying Settings",
|
||||
|
||||
@@ -124,12 +124,11 @@ frappe.ui.form.on("Request for Quotation",{
|
||||
frappe.urllib.get_full_url(
|
||||
"/api/method/erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_pdf?" +
|
||||
new URLSearchParams({
|
||||
doctype: frm.doc.doctype,
|
||||
name: frm.doc.name,
|
||||
supplier: data.supplier,
|
||||
print_format: data.print_format || "Standard",
|
||||
language: data.language || frappe.boot.lang,
|
||||
letter_head: data.letter_head || frm.doc.letter_head || "",
|
||||
letterhead: data.letter_head || frm.doc.letter_head || "",
|
||||
}).toString()
|
||||
)
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
@@ -388,24 +389,26 @@ def create_rfq_items(sq_doc, supplier, data):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_pdf(doctype, name, supplier, print_format=None, language=None, letter_head=None):
|
||||
# permissions get checked in `download_pdf`
|
||||
if doc := get_rfq_doc(doctype, name, supplier):
|
||||
download_pdf(
|
||||
doctype,
|
||||
name,
|
||||
print_format,
|
||||
doc=doc,
|
||||
language=language,
|
||||
letter_head=letter_head or None,
|
||||
)
|
||||
|
||||
|
||||
def get_rfq_doc(doctype, name, supplier):
|
||||
def get_pdf(
|
||||
name: str,
|
||||
supplier: str,
|
||||
print_format: Optional[str] = None,
|
||||
language: Optional[str] = None,
|
||||
letterhead: Optional[str] = None,
|
||||
):
|
||||
doc = frappe.get_doc("Request for Quotation", name)
|
||||
if supplier:
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
doc.update_supplier_part_no(supplier)
|
||||
return doc
|
||||
|
||||
# permissions get checked in `download_pdf`
|
||||
download_pdf(
|
||||
doc.doctype,
|
||||
doc.name,
|
||||
print_format,
|
||||
doc=doc,
|
||||
language=language,
|
||||
letterhead=letterhead or None,
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -8,6 +8,7 @@ from frappe.utils import nowdate
|
||||
|
||||
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import (
|
||||
create_supplier_quotation,
|
||||
get_pdf,
|
||||
make_supplier_quotation_from_rfq,
|
||||
)
|
||||
from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
|
||||
@@ -124,6 +125,11 @@ class TestRequestforQuotation(FrappeTestCase):
|
||||
rfq.status = "Draft"
|
||||
rfq.submit()
|
||||
|
||||
def test_get_pdf(self):
|
||||
rfq = make_request_for_quotation()
|
||||
get_pdf(rfq.name, rfq.get("suppliers")[0].supplier)
|
||||
self.assertEqual(frappe.local.response.type, "pdf")
|
||||
|
||||
|
||||
def make_request_for_quotation(**args):
|
||||
"""
|
||||
|
||||
@@ -204,6 +204,12 @@ class AccountsController(TransactionBase):
|
||||
validate_einvoice_fields(self)
|
||||
|
||||
def on_trash(self):
|
||||
# delete references in 'Repost Payment Ledger'
|
||||
rpi = frappe.qb.DocType("Repost Payment Ledger Items")
|
||||
frappe.qb.from_(rpi).delete().where(
|
||||
(rpi.voucher_type == self.doctype) & (rpi.voucher_no == self.name)
|
||||
).run()
|
||||
|
||||
# delete sl and gl entries on deletion of transaction
|
||||
if frappe.db.get_single_value("Accounts Settings", "delete_linked_ledger_entries"):
|
||||
ple = frappe.qb.DocType("Payment Ledger Entry")
|
||||
|
||||
@@ -409,7 +409,14 @@ class SubcontractingController(StockController):
|
||||
if self.available_materials.get(key) and self.available_materials[key]["batch_no"]:
|
||||
new_rm_obj = None
|
||||
for batch_no, batch_qty in self.available_materials[key]["batch_no"].items():
|
||||
if batch_qty >= qty:
|
||||
if batch_qty >= qty or (
|
||||
rm_obj.consumed_qty == 0
|
||||
and self.backflush_based_on == "BOM"
|
||||
and len(self.available_materials[key]["batch_no"]) == 1
|
||||
):
|
||||
if rm_obj.consumed_qty == 0:
|
||||
self.__set_consumed_qty(rm_obj, qty)
|
||||
|
||||
self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty)
|
||||
self.available_materials[key]["batch_no"][batch_no] -= qty
|
||||
return
|
||||
|
||||
@@ -26,10 +26,11 @@
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2021-02-08 12:51:48.971517",
|
||||
"modified": "2023-02-10 00:51:44.973957",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Lead Source",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -58,5 +59,7 @@
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"translated_doctype": 1
|
||||
}
|
||||
@@ -18,10 +18,11 @@
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-05-20 12:22:01.866472",
|
||||
"modified": "2023-02-10 01:40:23.713390",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Sales Stage",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -40,5 +41,7 @@
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
"states": [],
|
||||
"track_changes": 1,
|
||||
"translated_doctype": 1
|
||||
}
|
||||
@@ -306,7 +306,6 @@ erpnext.patches.v13_0.set_per_billed_in_return_delivery_note
|
||||
execute:frappe.delete_doc("DocType", "Naming Series")
|
||||
erpnext.patches.v13_0.job_card_status_on_hold
|
||||
erpnext.patches.v14_0.copy_is_subcontracted_value_to_is_old_subcontracting_flow
|
||||
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
||||
erpnext.patches.v14_0.crm_ux_cleanup
|
||||
erpnext.patches.v14_0.migrate_existing_lead_notes_as_per_the_new_format
|
||||
erpnext.patches.v14_0.remove_india_localisation # 14-07-2022
|
||||
@@ -315,10 +314,9 @@ erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022
|
||||
erpnext.patches.v14_0.fix_crm_no_of_employees
|
||||
erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes
|
||||
erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries
|
||||
erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
|
||||
erpnext.patches.v13_0.update_schedule_type_in_loans
|
||||
erpnext.patches.v13_0.drop_unused_sle_index_parts
|
||||
erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization
|
||||
erpnext.patches.v13_0.update_schedule_type_in_loans
|
||||
erpnext.patches.v14_0.update_partial_tds_fields
|
||||
erpnext.patches.v14_0.create_incoterms_and_migrate_shipment
|
||||
erpnext.patches.v14_0.setup_clear_repost_logs
|
||||
@@ -326,4 +324,7 @@ erpnext.patches.v14_0.create_accounting_dimensions_for_payment_request
|
||||
erpnext.patches.v14_0.update_entry_type_for_journal_entry
|
||||
erpnext.patches.v14_0.change_autoname_for_tax_withheld_vouchers
|
||||
erpnext.patches.v14_0.update_asset_value_for_manual_depr_entries
|
||||
erpnext.patches.v14_0.set_pick_list_status
|
||||
erpnext.patches.v14_0.set_pick_list_status
|
||||
# below 2 migration patches should always run last
|
||||
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
||||
erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
def execute():
|
||||
from erpnext.setup.setup_wizard.operations.install_fixtures import default_sales_partner_type
|
||||
from erpnext.setup.setup_wizard.operations.install_fixtures import read_lines
|
||||
|
||||
frappe.reload_doc("selling", "doctype", "sales_partner_type")
|
||||
|
||||
frappe.local.lang = frappe.db.get_default("lang") or "en"
|
||||
|
||||
default_sales_partner_type = read_lines("sales_partner_type.txt")
|
||||
|
||||
for s in default_sales_partner_type:
|
||||
insert_sales_partner_type(_(s))
|
||||
insert_sales_partner_type(s)
|
||||
|
||||
# get partner type in existing forms (customized)
|
||||
# and create a document if not created
|
||||
|
||||
@@ -17,10 +17,11 @@ def execute():
|
||||
|
||||
for report in reports_to_delete:
|
||||
if frappe.db.exists("Report", report):
|
||||
delete_links_from_desktop_icons(report)
|
||||
delete_auto_email_reports(report)
|
||||
check_and_delete_linked_reports(report)
|
||||
|
||||
frappe.delete_doc("Report", report)
|
||||
frappe.delete_doc("Report", report, force=True)
|
||||
|
||||
|
||||
def delete_auto_email_reports(report):
|
||||
@@ -28,3 +29,10 @@ def delete_auto_email_reports(report):
|
||||
auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"])
|
||||
for auto_email_report in auto_email_reports:
|
||||
frappe.delete_doc("Auto Email Report", auto_email_report[0])
|
||||
|
||||
|
||||
def delete_links_from_desktop_icons(report):
|
||||
"""Check for one or multiple Desktop Icons and delete"""
|
||||
desktop_icons = frappe.db.get_values("Desktop Icon", {"_report": report}, ["name"])
|
||||
for desktop_icon in desktop_icons:
|
||||
frappe.delete_doc("Desktop Icon", desktop_icon[0], force=True)
|
||||
|
||||
@@ -408,7 +408,7 @@
|
||||
"depends_on": "eval:(doc.frequency == \"Daily\" && doc.collect_progress == true)",
|
||||
"fieldname": "daily_time_to_send",
|
||||
"fieldtype": "Time",
|
||||
"label": "Time to send"
|
||||
"label": "Daily Time to send"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)",
|
||||
@@ -421,7 +421,7 @@
|
||||
"depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)",
|
||||
"fieldname": "weekly_time_to_send",
|
||||
"fieldtype": "Time",
|
||||
"label": "Time to send"
|
||||
"label": "Weekly Time to send"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_45",
|
||||
@@ -451,7 +451,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"max_attachments": 4,
|
||||
"modified": "2022-06-23 16:45:06.108499",
|
||||
"modified": "2023-02-14 04:54:25.819620",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Project",
|
||||
@@ -497,4 +497,4 @@
|
||||
"timeline_field": "customer",
|
||||
"title_field": "project_name",
|
||||
"track_seen": 1
|
||||
}
|
||||
}
|
||||
@@ -282,21 +282,21 @@
|
||||
{
|
||||
"fieldname": "base_total_costing_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Costing Amount",
|
||||
"label": "Base Total Costing Amount",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "base_total_billable_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Billable Amount",
|
||||
"label": "Base Total Billable Amount",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "base_total_billed_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Billed Amount",
|
||||
"label": "Base Total Billed Amount",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -311,10 +311,11 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-06-15 22:08:53.930200",
|
||||
"modified": "2023-02-14 04:55:41.735991",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Timesheet",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -388,5 +389,6 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"states": [],
|
||||
"title_field": "title"
|
||||
}
|
||||
@@ -143,6 +143,12 @@ var get_payment_mode_account = function(frm, mode_of_payment, callback) {
|
||||
|
||||
cur_frm.cscript.account_head = function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
|
||||
if (doc.docstatus == 1) {
|
||||
// Should not trigger any changes on change post submit
|
||||
return;
|
||||
}
|
||||
|
||||
if(!d.charge_type && d.account_head){
|
||||
frappe.msgprint(__("Please select Charge Type first"));
|
||||
frappe.model.set_value(cdt, cdn, "account_head", "");
|
||||
|
||||
@@ -221,9 +221,9 @@ $.extend(erpnext.utils, {
|
||||
callback: function(r) {
|
||||
if (r.message && r.message.length) {
|
||||
r.message.forEach((dimension) => {
|
||||
let found = filters.some(el => el.fieldname === dimension['fieldname']);
|
||||
let existing_filter = filters.filter(el => el.fieldname === dimension['fieldname']);
|
||||
|
||||
if (!found) {
|
||||
if (!existing_filter.length) {
|
||||
filters.splice(index, 0, {
|
||||
"fieldname": dimension["fieldname"],
|
||||
"label": __(dimension["doctype"]),
|
||||
@@ -232,6 +232,11 @@ $.extend(erpnext.utils, {
|
||||
return frappe.db.get_link_options(dimension["doctype"], txt);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
existing_filter[0]['fieldtype'] = "MultiSelectList";
|
||||
existing_filter[0]['get_data'] = function(txt) {
|
||||
return frappe.db.get_link_options(dimension["doctype"], txt);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,123 +1,68 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:industry",
|
||||
"beta": 0,
|
||||
"creation": "2012-03-27 14:36:09",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:industry",
|
||||
"creation": "2012-03-27 14:36:09",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"industry"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "industry",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Industry",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "industry",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"fieldname": "industry",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Industry",
|
||||
"oldfieldname": "industry",
|
||||
"oldfieldtype": "Data",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-flag",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Industry Type",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"icon": "fa fa-flag",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2023-02-10 03:14:40.735763",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Industry Type",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales User"
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales Master Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales Master Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"track_seen": 0
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"translated_doctype": 1
|
||||
}
|
||||
@@ -1,94 +1,47 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:sales_partner_type",
|
||||
"beta": 0,
|
||||
"creation": "2018-06-11 13:15:57.404716",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"autoname": "field:sales_partner_type",
|
||||
"creation": "2018-06-11 13:15:57.404716",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"sales_partner_type"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "sales_partner_type",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Sales Partner Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldname": "sales_partner_type",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Sales Partner Type",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-06-11 13:45:13.554307",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Partner Type",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2023-02-10 01:00:20.110800",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Partner Type",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"translated_doctype": 1
|
||||
}
|
||||
@@ -216,7 +216,7 @@ def get_sales_order_details(company_list, filters):
|
||||
)
|
||||
|
||||
if filters.get("item_group"):
|
||||
query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_group))
|
||||
query = query.where(db_so_item.item_group == filters.item_group)
|
||||
|
||||
if filters.get("from_date"):
|
||||
query = query.where(db_so.transaction_date >= filters.from_date)
|
||||
@@ -225,7 +225,7 @@ def get_sales_order_details(company_list, filters):
|
||||
query = query.where(db_so.transaction_date <= filters.to_date)
|
||||
|
||||
if filters.get("item_code"):
|
||||
query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_code))
|
||||
query = query.where(db_so_item.item_code == filters.item_code)
|
||||
|
||||
if filters.get("customer"):
|
||||
query = query.where(db_so.customer == filters.customer)
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"icon": "fa fa-bookmark",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2022-06-28 17:10:26.853753",
|
||||
"modified": "2023-02-10 01:53:41.319386",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Designation",
|
||||
@@ -58,5 +58,6 @@
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
"states": [],
|
||||
"translated_doctype": 1
|
||||
}
|
||||
@@ -3,13 +3,17 @@
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe import _, qb
|
||||
from frappe.desk.notifications import clear_notifications
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint
|
||||
from frappe.utils import cint, create_batch
|
||||
|
||||
|
||||
class TransactionDeletionRecord(Document):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TransactionDeletionRecord, self).__init__(*args, **kwargs)
|
||||
self.batch_size = 5000
|
||||
|
||||
def validate(self):
|
||||
frappe.only_for("System Manager")
|
||||
self.validate_doctypes_to_be_ignored()
|
||||
@@ -155,8 +159,9 @@ class TransactionDeletionRecord(Document):
|
||||
"DocField", filters={"fieldtype": "Table", "parent": doctype}, pluck="options"
|
||||
)
|
||||
|
||||
for table in child_tables:
|
||||
frappe.db.delete(table, {"parent": ["in", parent_docs_to_be_deleted]})
|
||||
for batch in create_batch(parent_docs_to_be_deleted, self.batch_size):
|
||||
for table in child_tables:
|
||||
frappe.db.delete(table, {"parent": ["in", batch]})
|
||||
|
||||
def delete_docs_linked_with_specified_company(self, doctype, company_fieldname):
|
||||
frappe.db.delete(doctype, {company_fieldname: self.company})
|
||||
@@ -181,13 +186,16 @@ class TransactionDeletionRecord(Document):
|
||||
frappe.db.sql("""update `tabSeries` set current = %s where name=%s""", (last, prefix))
|
||||
|
||||
def delete_version_log(self, doctype, company_fieldname):
|
||||
frappe.db.sql(
|
||||
"""delete from `tabVersion` where ref_doctype=%s and docname in
|
||||
(select name from `tab{0}` where `{1}`=%s)""".format(
|
||||
doctype, company_fieldname
|
||||
),
|
||||
(doctype, self.company),
|
||||
)
|
||||
dt = qb.DocType(doctype)
|
||||
names = qb.from_(dt).select(dt.name).where(dt[company_fieldname] == self.company).run(as_list=1)
|
||||
names = [x[0] for x in names]
|
||||
|
||||
if names:
|
||||
versions = qb.DocType("Version")
|
||||
for batch in create_batch(names, self.batch_size):
|
||||
qb.from_(versions).delete().where(
|
||||
(versions.ref_doctype == doctype) & (versions.docname.isin(batch))
|
||||
).run()
|
||||
|
||||
def delete_communications(self, doctype, company_fieldname):
|
||||
reference_docs = frappe.get_all(doctype, filters={company_fieldname: self.company})
|
||||
@@ -199,7 +207,8 @@ class TransactionDeletionRecord(Document):
|
||||
)
|
||||
communication_names = [c.name for c in communications]
|
||||
|
||||
frappe.delete_doc("Communication", communication_names, ignore_permissions=True)
|
||||
for batch in create_batch(communication_names, self.batch_size):
|
||||
frappe.delete_doc("Communication", batch, ignore_permissions=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
31
erpnext/setup/setup_wizard/data/designation.txt
Normal file
31
erpnext/setup/setup_wizard/data/designation.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
Accountant
|
||||
Administrative Assistant
|
||||
Administrative Officer
|
||||
Analyst
|
||||
Associate
|
||||
Business Analyst
|
||||
Business Development Manager
|
||||
Consultant
|
||||
Chief Executive Officer
|
||||
Chief Financial Officer
|
||||
Chief Operating Officer
|
||||
Chief Technology Officer
|
||||
Customer Service Representative
|
||||
Designer
|
||||
Engineer
|
||||
Executive Assistant
|
||||
Finance Manager
|
||||
HR Manager
|
||||
Head of Marketing and Sales
|
||||
Manager
|
||||
Managing Director
|
||||
Marketing Manager
|
||||
Marketing Specialist
|
||||
President
|
||||
Product Manager
|
||||
Project Manager
|
||||
Researcher
|
||||
Sales Representative
|
||||
Secretary
|
||||
Software Developer
|
||||
Vice President
|
||||
@@ -1,57 +0,0 @@
|
||||
from frappe import _
|
||||
|
||||
|
||||
def get_industry_types():
|
||||
return [
|
||||
_("Accounting"),
|
||||
_("Advertising"),
|
||||
_("Aerospace"),
|
||||
_("Agriculture"),
|
||||
_("Airline"),
|
||||
_("Apparel & Accessories"),
|
||||
_("Automotive"),
|
||||
_("Banking"),
|
||||
_("Biotechnology"),
|
||||
_("Broadcasting"),
|
||||
_("Brokerage"),
|
||||
_("Chemical"),
|
||||
_("Computer"),
|
||||
_("Consulting"),
|
||||
_("Consumer Products"),
|
||||
_("Cosmetics"),
|
||||
_("Defense"),
|
||||
_("Department Stores"),
|
||||
_("Education"),
|
||||
_("Electronics"),
|
||||
_("Energy"),
|
||||
_("Entertainment & Leisure"),
|
||||
_("Executive Search"),
|
||||
_("Financial Services"),
|
||||
_("Food, Beverage & Tobacco"),
|
||||
_("Grocery"),
|
||||
_("Health Care"),
|
||||
_("Internet Publishing"),
|
||||
_("Investment Banking"),
|
||||
_("Legal"),
|
||||
_("Manufacturing"),
|
||||
_("Motion Picture & Video"),
|
||||
_("Music"),
|
||||
_("Newspaper Publishers"),
|
||||
_("Online Auctions"),
|
||||
_("Pension Funds"),
|
||||
_("Pharmaceuticals"),
|
||||
_("Private Equity"),
|
||||
_("Publishing"),
|
||||
_("Real Estate"),
|
||||
_("Retail & Wholesale"),
|
||||
_("Securities & Commodity Exchanges"),
|
||||
_("Service"),
|
||||
_("Soap & Detergent"),
|
||||
_("Software"),
|
||||
_("Sports"),
|
||||
_("Technology"),
|
||||
_("Telecommunications"),
|
||||
_("Television"),
|
||||
_("Transportation"),
|
||||
_("Venture Capital"),
|
||||
]
|
||||
51
erpnext/setup/setup_wizard/data/industry_type.txt
Normal file
51
erpnext/setup/setup_wizard/data/industry_type.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
Accounting
|
||||
Advertising
|
||||
Aerospace
|
||||
Agriculture
|
||||
Airline
|
||||
Apparel & Accessories
|
||||
Automotive
|
||||
Banking
|
||||
Biotechnology
|
||||
Broadcasting
|
||||
Brokerage
|
||||
Chemical
|
||||
Computer
|
||||
Consulting
|
||||
Consumer Products
|
||||
Cosmetics
|
||||
Defense
|
||||
Department Stores
|
||||
Education
|
||||
Electronics
|
||||
Energy
|
||||
Entertainment & Leisure
|
||||
Executive Search
|
||||
Financial Services
|
||||
Food, Beverage & Tobacco
|
||||
Grocery
|
||||
Health Care
|
||||
Internet Publishing
|
||||
Investment Banking
|
||||
Legal
|
||||
Manufacturing
|
||||
Motion Picture & Video
|
||||
Music
|
||||
Newspaper Publishers
|
||||
Online Auctions
|
||||
Pension Funds
|
||||
Pharmaceuticals
|
||||
Private Equity
|
||||
Publishing
|
||||
Real Estate
|
||||
Retail & Wholesale
|
||||
Securities & Commodity Exchanges
|
||||
Service
|
||||
Soap & Detergent
|
||||
Software
|
||||
Sports
|
||||
Technology
|
||||
Telecommunications
|
||||
Television
|
||||
Transportation
|
||||
Venture Capital
|
||||
10
erpnext/setup/setup_wizard/data/lead_source.txt
Normal file
10
erpnext/setup/setup_wizard/data/lead_source.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Existing Customer
|
||||
Reference
|
||||
Advertisement
|
||||
Cold Calling
|
||||
Exhibition
|
||||
Supplier Reference
|
||||
Mass Mailing
|
||||
Customer's Vendor
|
||||
Campaign
|
||||
Walk In
|
||||
7
erpnext/setup/setup_wizard/data/sales_partner_type.txt
Normal file
7
erpnext/setup/setup_wizard/data/sales_partner_type.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Channel Partner
|
||||
Distributor
|
||||
Dealer
|
||||
Agent
|
||||
Retailer
|
||||
Implementation Partner
|
||||
Reseller
|
||||
8
erpnext/setup/setup_wizard/data/sales_stage.txt
Normal file
8
erpnext/setup/setup_wizard/data/sales_stage.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Prospecting
|
||||
Qualification
|
||||
Needs Analysis
|
||||
Value Proposition
|
||||
Identifying Decision Makers
|
||||
Perception Analysis
|
||||
Proposal/Price Quote
|
||||
Negotiation/Review
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
@@ -16,28 +17,10 @@ from frappe.utils import cstr, getdate
|
||||
from erpnext.accounts.doctype.account.account import RootNotEditable
|
||||
from erpnext.regional.address_template.setup import set_up_address_templates
|
||||
|
||||
default_lead_sources = [
|
||||
"Existing Customer",
|
||||
"Reference",
|
||||
"Advertisement",
|
||||
"Cold Calling",
|
||||
"Exhibition",
|
||||
"Supplier Reference",
|
||||
"Mass Mailing",
|
||||
"Customer's Vendor",
|
||||
"Campaign",
|
||||
"Walk In",
|
||||
]
|
||||
|
||||
default_sales_partner_type = [
|
||||
"Channel Partner",
|
||||
"Distributor",
|
||||
"Dealer",
|
||||
"Agent",
|
||||
"Retailer",
|
||||
"Implementation Partner",
|
||||
"Reseller",
|
||||
]
|
||||
def read_lines(filename: str) -> list[str]:
|
||||
"""Return a list of lines from a file in the data directory."""
|
||||
return (Path(__file__).parent.parent / "data" / filename).read_text().splitlines()
|
||||
|
||||
|
||||
def install(country=None):
|
||||
@@ -85,7 +68,11 @@ def install(country=None):
|
||||
# Stock Entry Type
|
||||
{"doctype": "Stock Entry Type", "name": "Material Issue", "purpose": "Material Issue"},
|
||||
{"doctype": "Stock Entry Type", "name": "Material Receipt", "purpose": "Material Receipt"},
|
||||
{"doctype": "Stock Entry Type", "name": "Material Transfer", "purpose": "Material Transfer"},
|
||||
{
|
||||
"doctype": "Stock Entry Type",
|
||||
"name": "Material Transfer",
|
||||
"purpose": "Material Transfer",
|
||||
},
|
||||
{"doctype": "Stock Entry Type", "name": "Manufacture", "purpose": "Manufacture"},
|
||||
{"doctype": "Stock Entry Type", "name": "Repack", "purpose": "Repack"},
|
||||
{
|
||||
@@ -103,22 +90,6 @@ def install(country=None):
|
||||
"name": "Material Consumption for Manufacture",
|
||||
"purpose": "Material Consumption for Manufacture",
|
||||
},
|
||||
# Designation
|
||||
{"doctype": "Designation", "designation_name": _("CEO")},
|
||||
{"doctype": "Designation", "designation_name": _("Manager")},
|
||||
{"doctype": "Designation", "designation_name": _("Analyst")},
|
||||
{"doctype": "Designation", "designation_name": _("Engineer")},
|
||||
{"doctype": "Designation", "designation_name": _("Accountant")},
|
||||
{"doctype": "Designation", "designation_name": _("Secretary")},
|
||||
{"doctype": "Designation", "designation_name": _("Associate")},
|
||||
{"doctype": "Designation", "designation_name": _("Administrative Officer")},
|
||||
{"doctype": "Designation", "designation_name": _("Business Development Manager")},
|
||||
{"doctype": "Designation", "designation_name": _("HR Manager")},
|
||||
{"doctype": "Designation", "designation_name": _("Project Manager")},
|
||||
{"doctype": "Designation", "designation_name": _("Head of Marketing and Sales")},
|
||||
{"doctype": "Designation", "designation_name": _("Software Developer")},
|
||||
{"doctype": "Designation", "designation_name": _("Designer")},
|
||||
{"doctype": "Designation", "designation_name": _("Researcher")},
|
||||
# territory: with two default territories, one for home country and one named Rest of the World
|
||||
{
|
||||
"doctype": "Territory",
|
||||
@@ -291,28 +262,18 @@ def install(country=None):
|
||||
{"doctype": "Market Segment", "market_segment": _("Lower Income")},
|
||||
{"doctype": "Market Segment", "market_segment": _("Middle Income")},
|
||||
{"doctype": "Market Segment", "market_segment": _("Upper Income")},
|
||||
# Sales Stages
|
||||
{"doctype": "Sales Stage", "stage_name": _("Prospecting")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Qualification")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Needs Analysis")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Value Proposition")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Identifying Decision Makers")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Perception Analysis")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")},
|
||||
# Warehouse Type
|
||||
{"doctype": "Warehouse Type", "name": "Transit"},
|
||||
]
|
||||
|
||||
from erpnext.setup.setup_wizard.data.industry_type import get_industry_types
|
||||
|
||||
records += [{"doctype": "Industry Type", "industry": d} for d in get_industry_types()]
|
||||
# records += [{"doctype":"Operation", "operation": d} for d in get_operations()]
|
||||
records += [{"doctype": "Lead Source", "source_name": _(d)} for d in default_lead_sources]
|
||||
|
||||
records += [
|
||||
{"doctype": "Sales Partner Type", "sales_partner_type": _(d)} for d in default_sales_partner_type
|
||||
]
|
||||
for doctype, title_field, filename in (
|
||||
("Designation", "designation_name", "designation.txt"),
|
||||
("Sales Stage", "stage_name", "sales_stage.txt"),
|
||||
("Industry Type", "industry", "industry_type.txt"),
|
||||
("Lead Source", "source_name", "lead_source.txt"),
|
||||
("Sales Partner Type", "sales_partner_type", "sales_partner_type.txt"),
|
||||
):
|
||||
records += [{"doctype": doctype, title_field: title} for title in read_lines(filename)]
|
||||
|
||||
base_path = frappe.get_app_path("erpnext", "stock", "doctype")
|
||||
response = frappe.read_file(
|
||||
@@ -397,7 +358,8 @@ def add_uom_data():
|
||||
frappe.get_doc({"doctype": "UOM Category", "category_name": _(d.get("category"))}).db_insert()
|
||||
|
||||
if not frappe.db.exists(
|
||||
"UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}
|
||||
"UOM Conversion Factor",
|
||||
{"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))},
|
||||
):
|
||||
frappe.get_doc(
|
||||
{
|
||||
@@ -535,7 +497,8 @@ def create_bank_account(args):
|
||||
|
||||
company_name = args.get("company_name")
|
||||
bank_account_group = frappe.db.get_value(
|
||||
"Account", {"account_type": "Bank", "is_group": 1, "root_type": "Asset", "company": company_name}
|
||||
"Account",
|
||||
{"account_type": "Bank", "is_group": 1, "root_type": "Asset", "company": company_name},
|
||||
)
|
||||
if bank_account_group:
|
||||
bank_account = frappe.get_doc(
|
||||
|
||||
@@ -97,12 +97,12 @@ frappe.ui.form.on("Delivery Note", {
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
|
||||
let internal = me.frm.doc.is_internal_customer;
|
||||
let internal = frm.doc.is_internal_customer;
|
||||
if (internal) {
|
||||
let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" :
|
||||
let button_label = (frm.doc.company === frm.doc.represents_company) ? "Internal Purchase Receipt" :
|
||||
"Inter Company Purchase Receipt";
|
||||
|
||||
me.frm.add_custom_button(button_label, function() {
|
||||
frm.add_custom_button(__(button_label), function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
|
||||
frm: frm,
|
||||
|
||||
@@ -521,6 +521,7 @@
|
||||
"allow_bulk_edit": 1,
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Delivery Note Item",
|
||||
"oldfieldname": "delivery_note_details",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Delivery Note Item",
|
||||
@@ -666,6 +667,7 @@
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"fieldtype": "Table",
|
||||
"label": "Sales Taxes and Charges",
|
||||
"oldfieldname": "other_charges",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Sales Taxes and Charges"
|
||||
@@ -1401,7 +1403,7 @@
|
||||
"idx": 146,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-12-12 18:38:53.067799",
|
||||
"modified": "2023-02-14 04:45:44.179670",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
|
||||
@@ -14,7 +14,7 @@ frappe.listview_settings['Delivery Note'] = {
|
||||
return [__("Completed"), "green", "per_billed,=,100"];
|
||||
}
|
||||
},
|
||||
onload: function (listview) {
|
||||
onload: function (doclist) {
|
||||
const action = () => {
|
||||
const selected_docs = doclist.get_checked_items();
|
||||
const docnames = doclist.get_checked_items(true);
|
||||
@@ -56,14 +56,14 @@ frappe.listview_settings['Delivery Note'] = {
|
||||
|
||||
// doclist.page.add_actions_menu_item(__('Create Delivery Trip'), action, false);
|
||||
|
||||
listview.page.add_action_item(__('Create Delivery Trip'), action);
|
||||
doclist.page.add_action_item(__('Create Delivery Trip'), action);
|
||||
|
||||
listview.page.add_action_item(__("Sales Invoice"), ()=>{
|
||||
erpnext.bulk_transaction_processing.create(listview, "Delivery Note", "Sales Invoice");
|
||||
doclist.page.add_action_item(__("Sales Invoice"), ()=>{
|
||||
erpnext.bulk_transaction_processing.create(doclist, "Delivery Note", "Sales Invoice");
|
||||
});
|
||||
|
||||
listview.page.add_action_item(__("Packaging Slip From Delivery Note"), ()=>{
|
||||
erpnext.bulk_transaction_processing.create(listview, "Delivery Note", "Packing Slip");
|
||||
doclist.page.add_action_item(__("Packaging Slip From Delivery Note"), ()=>{
|
||||
erpnext.bulk_transaction_processing.create(doclist, "Delivery Note", "Packing Slip");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -706,7 +706,7 @@
|
||||
"depends_on": "enable_deferred_expense",
|
||||
"fieldname": "no_of_months_exp",
|
||||
"fieldtype": "Int",
|
||||
"label": "No of Months"
|
||||
"label": "No of Months (Expense)"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
@@ -911,7 +911,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"make_attachments_public": 1,
|
||||
"modified": "2022-09-13 04:08:17.431731",
|
||||
"modified": "2023-02-14 04:48:26.343620",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
|
||||
@@ -108,10 +108,13 @@ frappe.ui.form.on('Material Request', {
|
||||
() => frm.events.create_pick_list(frm), __('Create'));
|
||||
}
|
||||
|
||||
if (frm.doc.material_request_type === "Material Transfer") {
|
||||
if (frm.doc.material_request_type === 'Material Transfer') {
|
||||
add_create_pick_list_button();
|
||||
frm.add_custom_button(__("Transfer Material"),
|
||||
frm.add_custom_button(__('Material Transfer'),
|
||||
() => frm.events.make_stock_entry(frm), __('Create'));
|
||||
|
||||
frm.add_custom_button(__('Material Transfer (In Transit)'),
|
||||
() => frm.events.make_in_transit_stock_entry(frm), __('Create'));
|
||||
}
|
||||
|
||||
if (frm.doc.material_request_type === "Material Issue") {
|
||||
@@ -333,6 +336,46 @@ frappe.ui.form.on('Material Request', {
|
||||
});
|
||||
},
|
||||
|
||||
make_in_transit_stock_entry(frm) {
|
||||
frappe.prompt(
|
||||
[
|
||||
{
|
||||
label: __('In Transit Warehouse'),
|
||||
fieldname: 'in_transit_warehouse',
|
||||
fieldtype: 'Link',
|
||||
options: 'Warehouse',
|
||||
reqd: 1,
|
||||
get_query: () => {
|
||||
return{
|
||||
filters: {
|
||||
'company': frm.doc.company,
|
||||
'is_group': 0,
|
||||
'warehouse_type': 'Transit'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
(values) => {
|
||||
frappe.call({
|
||||
method: "erpnext.stock.doctype.material_request.material_request.make_in_transit_stock_entry",
|
||||
args: {
|
||||
source_name: frm.doc.name,
|
||||
in_transit_warehouse: values.in_transit_warehouse
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
let doc = frappe.model.sync(r.message);
|
||||
frappe.set_route('Form', doc[0].doctype, doc[0].name);
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
__('In Transit Transfer'),
|
||||
__('Create Stock Entry')
|
||||
)
|
||||
},
|
||||
|
||||
create_pick_list: (frm) => {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.stock.doctype.material_request.material_request.create_pick_list",
|
||||
|
||||
@@ -719,3 +719,14 @@ def create_pick_list(source_name, target_doc=None):
|
||||
doc.set_item_locations()
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_in_transit_stock_entry(source_name, in_transit_warehouse):
|
||||
ste_doc = make_stock_entry(source_name)
|
||||
ste_doc.add_to_transit = 1
|
||||
|
||||
for row in ste_doc.items:
|
||||
row.t_warehouse = in_transit_warehouse
|
||||
|
||||
return ste_doc
|
||||
|
||||
@@ -11,6 +11,7 @@ from frappe.utils import flt, today
|
||||
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
from erpnext.stock.doctype.material_request.material_request import (
|
||||
make_in_transit_stock_entry,
|
||||
make_purchase_order,
|
||||
make_stock_entry,
|
||||
make_supplier_quotation,
|
||||
@@ -56,6 +57,22 @@ class TestMaterialRequest(FrappeTestCase):
|
||||
self.assertEqual(se.doctype, "Stock Entry")
|
||||
self.assertEqual(len(se.get("items")), len(mr.get("items")))
|
||||
|
||||
def test_in_transit_make_stock_entry(self):
|
||||
mr = frappe.copy_doc(test_records[0]).insert()
|
||||
|
||||
self.assertRaises(frappe.ValidationError, make_stock_entry, mr.name)
|
||||
|
||||
mr = frappe.get_doc("Material Request", mr.name)
|
||||
mr.material_request_type = "Material Transfer"
|
||||
mr.submit()
|
||||
|
||||
in_transit_warehouse = get_in_transit_warehouse(mr.company)
|
||||
se = make_in_transit_stock_entry(mr.name, in_transit_warehouse)
|
||||
|
||||
self.assertEqual(se.doctype, "Stock Entry")
|
||||
for row in se.get("items"):
|
||||
self.assertEqual(row.t_warehouse, in_transit_warehouse)
|
||||
|
||||
def _insert_stock_entry(self, qty1, qty2, warehouse=None):
|
||||
se = frappe.get_doc(
|
||||
{
|
||||
@@ -742,6 +759,36 @@ class TestMaterialRequest(FrappeTestCase):
|
||||
self.assertEqual(existing_requested_qty, current_requested_qty)
|
||||
|
||||
|
||||
def get_in_transit_warehouse(company):
|
||||
if not frappe.db.exists("Warehouse Type", "Transit"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Warehouse Type",
|
||||
"name": "Transit",
|
||||
}
|
||||
).insert()
|
||||
|
||||
in_transit_warehouse = frappe.db.exists(
|
||||
"Warehouse", {"warehouse_type": "Transit", "company": company}
|
||||
)
|
||||
|
||||
if not in_transit_warehouse:
|
||||
in_transit_warehouse = (
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Warehouse",
|
||||
"warehouse_name": "Transit",
|
||||
"warehouse_type": "Transit",
|
||||
"company": company,
|
||||
}
|
||||
)
|
||||
.insert()
|
||||
.name
|
||||
)
|
||||
|
||||
return in_transit_warehouse
|
||||
|
||||
|
||||
def make_material_request(**args):
|
||||
args = frappe._dict(args)
|
||||
mr = frappe.new_doc("Material Request")
|
||||
|
||||
@@ -887,18 +887,10 @@ def update_billing_percentage(pr_doc, update_modified=True):
|
||||
|
||||
# Update Billing % based on pending accepted qty
|
||||
total_amount, total_billed_amount = 0, 0
|
||||
for item in pr_doc.items:
|
||||
return_data = frappe.db.get_list(
|
||||
"Purchase Receipt",
|
||||
fields=["sum(abs(`tabPurchase Receipt Item`.qty)) as qty"],
|
||||
filters=[
|
||||
["Purchase Receipt", "docstatus", "=", 1],
|
||||
["Purchase Receipt", "is_return", "=", 1],
|
||||
["Purchase Receipt Item", "purchase_receipt_item", "=", item.name],
|
||||
],
|
||||
)
|
||||
item_wise_returned_qty = get_item_wise_returned_qty(pr_doc)
|
||||
|
||||
returned_qty = return_data[0].qty if return_data else 0
|
||||
for item in pr_doc.items:
|
||||
returned_qty = flt(item_wise_returned_qty.get(item.name))
|
||||
returned_amount = flt(returned_qty) * flt(item.rate)
|
||||
pending_amount = flt(item.amount) - returned_amount
|
||||
total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt
|
||||
@@ -915,6 +907,27 @@ def update_billing_percentage(pr_doc, update_modified=True):
|
||||
pr_doc.notify_update()
|
||||
|
||||
|
||||
def get_item_wise_returned_qty(pr_doc):
|
||||
items = [d.name for d in pr_doc.items]
|
||||
|
||||
return frappe._dict(
|
||||
frappe.get_all(
|
||||
"Purchase Receipt",
|
||||
fields=[
|
||||
"`tabPurchase Receipt Item`.purchase_receipt_item",
|
||||
"sum(abs(`tabPurchase Receipt Item`.qty)) as qty",
|
||||
],
|
||||
filters=[
|
||||
["Purchase Receipt", "docstatus", "=", 1],
|
||||
["Purchase Receipt", "is_return", "=", 1],
|
||||
["Purchase Receipt Item", "purchase_receipt_item", "in", items],
|
||||
],
|
||||
group_by="`tabPurchase Receipt Item`.purchase_receipt_item",
|
||||
as_list=1,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_purchase_invoice(source_name, target_doc=None):
|
||||
from erpnext.accounts.party import get_payment_terms_template
|
||||
|
||||
@@ -859,7 +859,8 @@
|
||||
"label": "Purchase Receipt Item",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
@@ -974,7 +975,8 @@
|
||||
"label": "Purchase Invoice Item",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "product_bundle",
|
||||
@@ -1010,7 +1012,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-11-02 12:49:28.746701",
|
||||
"modified": "2023-01-18 15:48:58.114923",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt Item",
|
||||
|
||||
@@ -2,7 +2,22 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Stock Reposting Settings', {
|
||||
// refresh: function(frm) {
|
||||
refresh: function(frm) {
|
||||
frm.trigger('convert_to_item_based_reposting');
|
||||
},
|
||||
|
||||
// }
|
||||
convert_to_item_based_reposting: function(frm) {
|
||||
frm.add_custom_button(__('Convert to Item Based Reposting'), function() {
|
||||
frm.call({
|
||||
method: 'convert_to_item_wh_reposting',
|
||||
frezz: true,
|
||||
doc: frm.doc,
|
||||
callback: function(r) {
|
||||
if (!r.exc) {
|
||||
frm.reload_doc();
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_to_date, get_datetime, get_time_str, time_diff_in_hours
|
||||
|
||||
@@ -24,3 +26,62 @@ class StockRepostingSettings(Document):
|
||||
|
||||
if diff < 10:
|
||||
self.end_time = get_time_str(add_to_date(self.start_time, hours=10, as_datetime=True))
|
||||
|
||||
@frappe.whitelist()
|
||||
def convert_to_item_wh_reposting(self):
|
||||
"""Convert Transaction reposting to Item Warehouse based reposting if Item Based Reposting has enabled."""
|
||||
|
||||
reposting_data = get_reposting_entries()
|
||||
|
||||
vouchers = [d.voucher_no for d in reposting_data]
|
||||
|
||||
item_warehouses = {}
|
||||
|
||||
for ledger in get_stock_ledgers(vouchers):
|
||||
key = (ledger.item_code, ledger.warehouse)
|
||||
if key not in item_warehouses:
|
||||
item_warehouses[key] = ledger.posting_date
|
||||
elif frappe.utils.getdate(item_warehouses.get(key)) > frappe.utils.getdate(ledger.posting_date):
|
||||
item_warehouses[key] = ledger.posting_date
|
||||
|
||||
for key, posting_date in item_warehouses.items():
|
||||
item_code, warehouse = key
|
||||
create_repost_item_valuation(item_code, warehouse, posting_date)
|
||||
|
||||
for row in reposting_data:
|
||||
frappe.db.set_value("Repost Item Valuation", row.name, "status", "Skipped")
|
||||
|
||||
self.db_set("item_based_reposting", 1)
|
||||
frappe.msgprint(_("Item Warehouse based reposting has been enabled."))
|
||||
|
||||
|
||||
def get_reposting_entries():
|
||||
return frappe.get_all(
|
||||
"Repost Item Valuation",
|
||||
fields=["voucher_no", "name"],
|
||||
filters={"status": ("in", ["Queued", "In Progress"]), "docstatus": 1, "based_on": "Transaction"},
|
||||
)
|
||||
|
||||
|
||||
def get_stock_ledgers(vouchers):
|
||||
return frappe.get_all(
|
||||
"Stock Ledger Entry",
|
||||
fields=["item_code", "warehouse", "posting_date"],
|
||||
filters={"voucher_no": ("in", vouchers)},
|
||||
)
|
||||
|
||||
|
||||
def create_repost_item_valuation(item_code, warehouse, posting_date):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Repost Item Valuation",
|
||||
"company": frappe.get_cached_value("Warehouse", warehouse, "company"),
|
||||
"posting_date": posting_date,
|
||||
"based_on": "Item and Warehouse",
|
||||
"posting_time": "00:00:01",
|
||||
"item_code": item_code,
|
||||
"warehouse": warehouse,
|
||||
"allow_negative_stock": True,
|
||||
"status": "Queued",
|
||||
}
|
||||
).submit()
|
||||
|
||||
@@ -306,7 +306,7 @@ def get_stock_ledger_entries(filters, items):
|
||||
query = query.where(sle.item_code.isin(items))
|
||||
|
||||
for field in ["voucher_no", "batch_no", "project", "company"]:
|
||||
if filters.get(field):
|
||||
if filters.get(field) and field not in inventory_dimension_fields:
|
||||
query = query.where(sle[field] == filters.get(field))
|
||||
|
||||
query = apply_warehouse_filter(query, sle, filters)
|
||||
|
||||
@@ -121,7 +121,7 @@ def get_reserved_qty(item_code, warehouse):
|
||||
and parenttype='Sales Order'
|
||||
and item_code != parent_item
|
||||
and exists (select * from `tabSales Order` so
|
||||
where name = dnpi_in.parent and docstatus = 1 and status != 'Closed')
|
||||
where name = dnpi_in.parent and docstatus = 1 and status not in ('On Hold', 'Closed'))
|
||||
) dnpi)
|
||||
union
|
||||
(select stock_qty as dnpi_qty, qty as so_item_qty,
|
||||
@@ -131,7 +131,7 @@ def get_reserved_qty(item_code, warehouse):
|
||||
and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0)
|
||||
and exists(select * from `tabSales Order` so
|
||||
where so.name = so_item.parent and so.docstatus = 1
|
||||
and so.status != 'Closed'))
|
||||
and so.status not in ('On Hold', 'Closed')))
|
||||
) tab
|
||||
where
|
||||
so_item_qty >= so_item_delivered_qty
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# See license.txt
|
||||
|
||||
import copy
|
||||
from collections import defaultdict
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
@@ -186,6 +187,40 @@ class TestSubcontractingOrder(FrappeTestCase):
|
||||
)
|
||||
self.assertEqual(len(ste.items), len(rm_items))
|
||||
|
||||
def test_make_rm_stock_entry_for_batch_items_with_less_transfer(self):
|
||||
set_backflush_based_on("BOM")
|
||||
|
||||
service_items = [
|
||||
{
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"item_code": "Subcontracted Service Item 4",
|
||||
"qty": 5,
|
||||
"rate": 100,
|
||||
"fg_item": "Subcontracted Item SA4",
|
||||
"fg_item_qty": 5,
|
||||
}
|
||||
]
|
||||
|
||||
sco = get_subcontracting_order(service_items=service_items)
|
||||
rm_items = get_rm_items(sco.supplied_items)
|
||||
itemwise_details = make_stock_in_entry(rm_items=rm_items)
|
||||
|
||||
itemwise_transfer_qty = defaultdict(int)
|
||||
for item in rm_items:
|
||||
item["qty"] -= 1
|
||||
itemwise_transfer_qty[item["item_code"]] += item["qty"]
|
||||
|
||||
ste = make_stock_transfer_entry(
|
||||
sco_no=sco.name,
|
||||
rm_items=rm_items,
|
||||
itemwise_details=copy.deepcopy(itemwise_details),
|
||||
)
|
||||
|
||||
scr = make_subcontracting_receipt(sco.name)
|
||||
|
||||
for row in scr.supplied_items:
|
||||
self.assertEqual(row.consumed_qty, itemwise_transfer_qty.get(row.rm_item_code) + 1)
|
||||
|
||||
def test_update_reserved_qty_for_subcontracting(self):
|
||||
# Create RM Material Receipt
|
||||
make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item", qty=10, basic_rate=100)
|
||||
|
||||
@@ -51,13 +51,31 @@ frappe.ui.form.on('Subcontracting Receipt', {
|
||||
}
|
||||
}));
|
||||
|
||||
frm.set_query("expense_account", "items", function () {
|
||||
frm.set_query('expense_account', 'items', function () {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.get_expense_account",
|
||||
query: 'erpnext.controllers.queries.get_expense_account',
|
||||
filters: { 'company': frm.doc.company }
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
return {
|
||||
filters: {
|
||||
item: row.item_code
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let batch_no_field = frm.get_docfield("items", "batch_no");
|
||||
if (batch_no_field) {
|
||||
batch_no_field.get_route_options_for_new_doc = function(row) {
|
||||
return {
|
||||
"item": row.doc.item_code
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
frappe.db.get_single_value('Buying Settings', 'backflush_raw_materials_of_subcontract_based_on').then(val => {
|
||||
if (val == 'Material Transferred for Subcontract') {
|
||||
frm.fields_dict['supplied_items'].grid.grid_rows.forEach((grid_row) => {
|
||||
@@ -73,7 +91,7 @@ frappe.ui.form.on('Subcontracting Receipt', {
|
||||
|
||||
refresh: (frm) => {
|
||||
if (frm.doc.docstatus > 0) {
|
||||
frm.add_custom_button(__("Stock Ledger"), function () {
|
||||
frm.add_custom_button(__('Stock Ledger'), function () {
|
||||
frappe.route_options = {
|
||||
voucher_no: frm.doc.name,
|
||||
from_date: frm.doc.posting_date,
|
||||
@@ -81,8 +99,8 @@ frappe.ui.form.on('Subcontracting Receipt', {
|
||||
company: frm.doc.company,
|
||||
show_cancelled_entries: frm.doc.docstatus === 2
|
||||
};
|
||||
frappe.set_route("query-report", "Stock Ledger");
|
||||
}, __("View"));
|
||||
frappe.set_route('query-report', 'Stock Ledger');
|
||||
}, __('View'));
|
||||
|
||||
frm.add_custom_button(__('Accounting Ledger'), function () {
|
||||
frappe.route_options = {
|
||||
@@ -90,11 +108,11 @@ frappe.ui.form.on('Subcontracting Receipt', {
|
||||
from_date: frm.doc.posting_date,
|
||||
to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
|
||||
company: frm.doc.company,
|
||||
group_by: "Group by Voucher (Consolidated)",
|
||||
group_by: 'Group by Voucher (Consolidated)',
|
||||
show_cancelled_entries: frm.doc.docstatus === 2
|
||||
};
|
||||
frappe.set_route("query-report", "General Ledger");
|
||||
}, __("View"));
|
||||
frappe.set_route('query-report', 'General Ledger');
|
||||
}, __('View'));
|
||||
}
|
||||
|
||||
if (!frm.doc.is_return && frm.doc.docstatus == 1 && frm.doc.per_returned < 100) {
|
||||
@@ -111,25 +129,25 @@ frappe.ui.form.on('Subcontracting Receipt', {
|
||||
frm.add_custom_button(__('Subcontracting Order'), function () {
|
||||
if (!frm.doc.supplier) {
|
||||
frappe.throw({
|
||||
title: __("Mandatory"),
|
||||
message: __("Please Select a Supplier")
|
||||
title: __('Mandatory'),
|
||||
message: __('Please Select a Supplier')
|
||||
});
|
||||
}
|
||||
|
||||
erpnext.utils.map_current_doc({
|
||||
method: 'erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.make_subcontracting_receipt',
|
||||
source_doctype: "Subcontracting Order",
|
||||
source_doctype: 'Subcontracting Order',
|
||||
target: frm,
|
||||
setters: {
|
||||
supplier: frm.doc.supplier,
|
||||
},
|
||||
get_query_filters: {
|
||||
docstatus: 1,
|
||||
per_received: ["<", 100],
|
||||
per_received: ['<', 100],
|
||||
company: frm.doc.company
|
||||
}
|
||||
});
|
||||
}, __("Get Items From"));
|
||||
}, __('Get Items From'));
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -34,16 +34,18 @@
|
||||
</a>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-column col-sm-6">
|
||||
<div class="page-header-actions-block" data-html-block="header-actions">
|
||||
<p>
|
||||
<a href="/api/method/erpnext.accounts.doctype.payment_request.payment_request.make_payment_request?dn={{ doc.name }}&dt={{ doc.doctype }}&submit_doc=1&order_type=Shopping Cart"
|
||||
class="btn btn-primary btn-sm" id="pay-for-order">
|
||||
{{ _("Pay") }} {{doc.get_formatted("grand_total") }}
|
||||
</a>
|
||||
</p>
|
||||
{% if show_pay_button %}
|
||||
<div class="form-column col-sm-6">
|
||||
<div class="page-header-actions-block" data-html-block="header-actions">
|
||||
<p>
|
||||
<a href="/api/method/erpnext.accounts.doctype.payment_request.payment_request.make_payment_request?dn={{ doc.name }}&dt={{ doc.doctype }}&submit_doc=1&order_type=Shopping Cart"
|
||||
class="btn btn-primary btn-sm" id="pay-for-order">
|
||||
{{ _("Pay") }} {{doc.get_formatted("grand_total") }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ def get_context(context):
|
||||
)
|
||||
context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points"))
|
||||
|
||||
context.show_pay_button = frappe.db.get_single_value("Buying Settings", "show_pay_button")
|
||||
context.show_make_pi_button = False
|
||||
if context.doc.get("supplier"):
|
||||
# show Make Purchase Invoice button based on permission
|
||||
|
||||
Reference in New Issue
Block a user