mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 00:14:50 +00:00
Merge pull request #38086 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -112,7 +112,8 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
|
|
||||||
for party in parties:
|
for party in parties:
|
||||||
filters = {"status": "Active"} if party == "Employee" else {"disabled": 0}
|
filters = {"status": "Active"} if party == "Employee" else {"disabled": 0}
|
||||||
names = frappe.get_all(party, filters=filters, pluck=party.lower() + "_name")
|
field = party.lower() + "_name"
|
||||||
|
names = frappe.get_all(party, filters=filters, fields=[f"{field} as party_name", "name"])
|
||||||
|
|
||||||
for field in ["bank_party_name", "description"]:
|
for field in ["bank_party_name", "description"]:
|
||||||
if not self.get(field):
|
if not self.get(field):
|
||||||
@@ -131,7 +132,11 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
|
|
||||||
def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
|
def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
|
||||||
skip = False
|
skip = False
|
||||||
result = process.extract(query=self.get(field), choices=names, scorer=fuzz.token_set_ratio)
|
result = process.extract(
|
||||||
|
query=self.get(field),
|
||||||
|
choices={row.get("name"): row.get("party_name") for row in names},
|
||||||
|
scorer=fuzz.token_set_ratio,
|
||||||
|
)
|
||||||
party_name, skip = self.process_fuzzy_result(result)
|
party_name, skip = self.process_fuzzy_result(result)
|
||||||
|
|
||||||
if not party_name:
|
if not party_name:
|
||||||
@@ -149,14 +154,14 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
|
|
||||||
Returns: Result, Skip (whether or not to discontinue matching)
|
Returns: Result, Skip (whether or not to discontinue matching)
|
||||||
"""
|
"""
|
||||||
PARTY, SCORE, CUTOFF = 0, 1, 80
|
SCORE, PARTY_ID, CUTOFF = 1, 2, 80
|
||||||
|
|
||||||
if not result or not len(result):
|
if not result or not len(result):
|
||||||
return None, False
|
return None, False
|
||||||
|
|
||||||
first_result = result[0]
|
first_result = result[0]
|
||||||
if len(result) == 1:
|
if len(result) == 1:
|
||||||
return (first_result[PARTY] if first_result[SCORE] > CUTOFF else None), True
|
return (first_result[PARTY_ID] if first_result[SCORE] > CUTOFF else None), True
|
||||||
|
|
||||||
second_result = result[1]
|
second_result = result[1]
|
||||||
if first_result[SCORE] > CUTOFF:
|
if first_result[SCORE] > CUTOFF:
|
||||||
@@ -165,7 +170,7 @@ class AutoMatchbyPartyNameDescription:
|
|||||||
if first_result[SCORE] == second_result[SCORE]:
|
if first_result[SCORE] == second_result[SCORE]:
|
||||||
return None, True
|
return None, True
|
||||||
|
|
||||||
return first_result[PARTY], True
|
return first_result[PARTY_ID], True
|
||||||
else:
|
else:
|
||||||
return None, False
|
return None, False
|
||||||
|
|
||||||
|
|||||||
@@ -53,10 +53,18 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
of Accounts. Please enter the account names and add more rows as per your requirement.`);
|
of Accounts. Please enter the account names and add more rows as per your requirement.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
label : "Company",
|
||||||
|
fieldname: "company",
|
||||||
|
fieldtype: "Link",
|
||||||
|
reqd: 1,
|
||||||
|
hidden: 1,
|
||||||
|
default: frm.doc.company,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function() {
|
||||||
var data = d.get_values();
|
let data = d.get_values();
|
||||||
|
|
||||||
if (!data.template_type) {
|
if (!data.template_type) {
|
||||||
frappe.throw(__('Please select <b>Template Type</b> to download template'));
|
frappe.throw(__('Please select <b>Template Type</b> to download template'));
|
||||||
@@ -66,7 +74,8 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
'/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template',
|
'/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template',
|
||||||
{
|
{
|
||||||
file_type: data.file_type,
|
file_type: data.file_type,
|
||||||
template_type: data.template_type
|
template_type: data.template_type,
|
||||||
|
company: data.company
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from functools import reduce
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.desk.form.linked_with import get_linked_fields
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cint, cstr
|
from frappe.utils import cint, cstr
|
||||||
from frappe.utils.csvutils import UnicodeWriter
|
from frappe.utils.csvutils import UnicodeWriter
|
||||||
@@ -294,10 +295,8 @@ def build_response_as_excel(writer):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def download_template(file_type, template_type):
|
def download_template(file_type, template_type, company):
|
||||||
data = frappe._dict(frappe.local.form_dict)
|
writer = get_template(template_type, company)
|
||||||
|
|
||||||
writer = get_template(template_type)
|
|
||||||
|
|
||||||
if file_type == "CSV":
|
if file_type == "CSV":
|
||||||
# download csv file
|
# download csv file
|
||||||
@@ -308,8 +307,7 @@ def download_template(file_type, template_type):
|
|||||||
build_response_as_excel(writer)
|
build_response_as_excel(writer)
|
||||||
|
|
||||||
|
|
||||||
def get_template(template_type):
|
def get_template(template_type, company):
|
||||||
|
|
||||||
fields = [
|
fields = [
|
||||||
"Account Name",
|
"Account Name",
|
||||||
"Parent Account",
|
"Parent Account",
|
||||||
@@ -335,34 +333,17 @@ def get_template(template_type):
|
|||||||
["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
|
["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
writer = get_sample_template(writer)
|
writer = get_sample_template(writer, company)
|
||||||
|
|
||||||
return writer
|
return writer
|
||||||
|
|
||||||
|
|
||||||
def get_sample_template(writer):
|
def get_sample_template(writer, company):
|
||||||
template = [
|
currency = frappe.db.get_value("Company", company, "default_currency")
|
||||||
["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"],
|
with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f:
|
||||||
["Sources Of Funds(Liabilities)", "", "", "", 1, "", "Liability"],
|
for row in f:
|
||||||
["Equity", "", "", "", 1, "", "Equity"],
|
row = row.strip().split(",") + [currency]
|
||||||
["Expenses", "", "", "", 1, "", "Expense"],
|
writer.writerow(row)
|
||||||
["Income", "", "", "", 1, "", "Income"],
|
|
||||||
["Bank Accounts", "Application Of Funds(Assets)", "", "", 1, "Bank", "Asset"],
|
|
||||||
["Cash In Hand", "Application Of Funds(Assets)", "", "", 1, "Cash", "Asset"],
|
|
||||||
["Stock Assets", "Application Of Funds(Assets)", "", "", 1, "Stock", "Asset"],
|
|
||||||
["Cost Of Goods Sold", "Expenses", "", "", 0, "Cost of Goods Sold", "Expense"],
|
|
||||||
["Asset Depreciation", "Expenses", "", "", 0, "Depreciation", "Expense"],
|
|
||||||
["Fixed Assets", "Application Of Funds(Assets)", "", "", 0, "Fixed Asset", "Asset"],
|
|
||||||
["Accounts Payable", "Sources Of Funds(Liabilities)", "", "", 0, "Payable", "Liability"],
|
|
||||||
["Accounts Receivable", "Application Of Funds(Assets)", "", "", 1, "Receivable", "Asset"],
|
|
||||||
["Stock Expenses", "Expenses", "", "", 0, "Stock Adjustment", "Expense"],
|
|
||||||
["Sample Bank", "Bank Accounts", "", "", 0, "Bank", "Asset"],
|
|
||||||
["Cash", "Cash In Hand", "", "", 0, "Cash", "Asset"],
|
|
||||||
["Stores", "Stock Assets", "", "", 0, "Stock", "Asset"],
|
|
||||||
]
|
|
||||||
|
|
||||||
for row in template:
|
|
||||||
writer.writerow(row)
|
|
||||||
|
|
||||||
return writer
|
return writer
|
||||||
|
|
||||||
@@ -453,14 +434,11 @@ def get_mandatory_account_types():
|
|||||||
|
|
||||||
|
|
||||||
def unset_existing_data(company):
|
def unset_existing_data(company):
|
||||||
linked = frappe.db.sql(
|
|
||||||
'''select fieldname from tabDocField
|
|
||||||
where fieldtype="Link" and options="Account" and parent="Company"''',
|
|
||||||
as_dict=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# remove accounts data from company
|
# remove accounts data from company
|
||||||
update_values = {d.fieldname: "" for d in linked}
|
|
||||||
|
fieldnames = get_linked_fields("Account").get("Company", {}).get("fieldname", [])
|
||||||
|
linked = [{"fieldname": name} for name in fieldnames]
|
||||||
|
update_values = {d.get("fieldname"): "" for d in linked}
|
||||||
frappe.db.set_value("Company", company, update_values, update_values)
|
frappe.db.set_value("Company", company, update_values, update_values)
|
||||||
|
|
||||||
# remove accounts data from various doctypes
|
# remove accounts data from various doctypes
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
Application Of Funds(Assets),,,,1,,Asset
|
||||||
|
Sources Of Funds(Liabilities),,,,1,,Liability
|
||||||
|
Equity,,,,1,,Equity
|
||||||
|
Expenses,,,,1,Expense Account,Expense
|
||||||
|
Income,,,,1,Income Account,Income
|
||||||
|
Bank Accounts,Application Of Funds(Assets),,,1,Bank,Asset
|
||||||
|
Cash In Hand,Application Of Funds(Assets),,,1,Cash,Asset
|
||||||
|
Stock Assets,Application Of Funds(Assets),,,1,Stock,Asset
|
||||||
|
Cost Of Goods Sold,Expenses,,,0,Cost of Goods Sold,Expense
|
||||||
|
Asset Depreciation,Expenses,,,0,Depreciation,Expense
|
||||||
|
Fixed Assets,Application Of Funds(Assets),,,0,Fixed Asset,Asset
|
||||||
|
Accounts Payable,Sources Of Funds(Liabilities),,,0,Payable,Liability
|
||||||
|
Accounts Receivable,Application Of Funds(Assets),,,1,Receivable,Asset
|
||||||
|
Stock Expenses,Expenses,,,0,Stock Adjustment,Expense
|
||||||
|
Sample Bank,Bank Accounts,,,0,Bank,Asset
|
||||||
|
Cash,Cash In Hand,,,0,Cash,Asset
|
||||||
|
Stores,Stock Assets,,,0,Stock,Asset
|
||||||
|
@@ -114,10 +114,13 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "customer_group",
|
"fieldname":"customer_group",
|
||||||
"label": __("Customer Group"),
|
"label": __("Customer Group"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "MultiSelectList",
|
||||||
"options": "Customer Group"
|
"options": "Customer Group",
|
||||||
|
get_data: function(txt) {
|
||||||
|
return frappe.db.get_link_options('Customer Group', txt);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "payment_terms_template",
|
"fieldname": "payment_terms_template",
|
||||||
|
|||||||
@@ -849,7 +849,13 @@ class ReceivablePayableReport(object):
|
|||||||
self.customer = qb.DocType("Customer")
|
self.customer = qb.DocType("Customer")
|
||||||
|
|
||||||
if self.filters.get("customer_group"):
|
if self.filters.get("customer_group"):
|
||||||
self.get_hierarchical_filters("Customer Group", "customer_group")
|
groups = get_customer_group_with_children(self.filters.customer_group)
|
||||||
|
customers = (
|
||||||
|
qb.from_(self.customer)
|
||||||
|
.select(self.customer.name)
|
||||||
|
.where(self.customer["customer_group"].isin(groups))
|
||||||
|
)
|
||||||
|
self.qb_selection_filter.append(self.ple.party.isin(customers))
|
||||||
|
|
||||||
if self.filters.get("territory"):
|
if self.filters.get("territory"):
|
||||||
self.get_hierarchical_filters("Territory", "territory")
|
self.get_hierarchical_filters("Territory", "territory")
|
||||||
@@ -1141,3 +1147,19 @@ class ReceivablePayableReport(object):
|
|||||||
.run()
|
.run()
|
||||||
)
|
)
|
||||||
self.err_journals = [x[0] for x in results] if results else []
|
self.err_journals = [x[0] for x in results] if results else []
|
||||||
|
|
||||||
|
|
||||||
|
def get_customer_group_with_children(customer_groups):
|
||||||
|
if not isinstance(customer_groups, list):
|
||||||
|
customer_groups = [d.strip() for d in customer_groups.strip().split(",") if d]
|
||||||
|
|
||||||
|
all_customer_groups = []
|
||||||
|
for d in customer_groups:
|
||||||
|
if frappe.db.exists("Customer Group", d):
|
||||||
|
lft, rgt = frappe.db.get_value("Customer Group", d, ["lft", "rgt"])
|
||||||
|
children = frappe.get_all("Customer Group", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
|
||||||
|
all_customer_groups += [c.name for c in children]
|
||||||
|
else:
|
||||||
|
frappe.throw(_("Customer Group: {0} does not exist").format(d))
|
||||||
|
|
||||||
|
return list(set(all_customer_groups))
|
||||||
|
|||||||
@@ -475,6 +475,30 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
|
|||||||
report = execute(filters)[1]
|
report = execute(filters)[1]
|
||||||
self.assertEqual(len(report), 0)
|
self.assertEqual(len(report), 0)
|
||||||
|
|
||||||
|
def test_multi_customer_group_filter(self):
|
||||||
|
si = self.create_sales_invoice()
|
||||||
|
cus_group = frappe.db.get_value("Customer", self.customer, "customer_group")
|
||||||
|
# Create a list of customer groups, e.g., ["Group1", "Group2"]
|
||||||
|
cus_groups_list = [cus_group, "_Test Customer Group 1"]
|
||||||
|
|
||||||
|
filters = {
|
||||||
|
"company": self.company,
|
||||||
|
"report_date": today(),
|
||||||
|
"range1": 30,
|
||||||
|
"range2": 60,
|
||||||
|
"range3": 90,
|
||||||
|
"range4": 120,
|
||||||
|
"customer_group": cus_groups_list, # Use the list of customer groups
|
||||||
|
}
|
||||||
|
report = execute(filters)[1]
|
||||||
|
|
||||||
|
# Assert that the report contains data for the specified customer groups
|
||||||
|
self.assertTrue(len(report) > 0)
|
||||||
|
|
||||||
|
for row in report:
|
||||||
|
# Assert that the customer group of each row is in the list of customer groups
|
||||||
|
self.assertIn(row.customer_group, cus_groups_list)
|
||||||
|
|
||||||
def test_party_account_filter(self):
|
def test_party_account_filter(self):
|
||||||
si1 = self.create_sales_invoice()
|
si1 = self.create_sales_invoice()
|
||||||
self.customer2 = (
|
self.customer2 = (
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ class BuyingController(SubcontractingController):
|
|||||||
{
|
{
|
||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
"warehouse": d.get("from_warehouse"),
|
"warehouse": d.get("from_warehouse"),
|
||||||
"posting_date": self.get("posting_date") or self.get("transation_date"),
|
"posting_date": self.get("posting_date") or self.get("transaction_date"),
|
||||||
"posting_time": posting_time,
|
"posting_time": posting_time,
|
||||||
"qty": -1 * flt(d.get("stock_qty")),
|
"qty": -1 * flt(d.get("stock_qty")),
|
||||||
"serial_no": d.get("serial_no"),
|
"serial_no": d.get("serial_no"),
|
||||||
|
|||||||
@@ -89,10 +89,6 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
frm.trigger("show_progress");
|
frm.trigger("show_progress");
|
||||||
|
|
||||||
if (frm.doc.status !== "Completed") {
|
if (frm.doc.status !== "Completed") {
|
||||||
frm.add_custom_button(__("Work Order Tree"), ()=> {
|
|
||||||
frappe.set_route('Tree', 'Work Order', {production_plan: frm.doc.name});
|
|
||||||
}, __('View'));
|
|
||||||
|
|
||||||
frm.add_custom_button(__("Production Plan Summary"), ()=> {
|
frm.add_custom_button(__("Production Plan Summary"), ()=> {
|
||||||
frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name});
|
frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name});
|
||||||
}, __('View'));
|
}, __('View'));
|
||||||
|
|||||||
@@ -827,8 +827,6 @@ class ProductionPlan(Document):
|
|||||||
# Combine subassembly items
|
# Combine subassembly items
|
||||||
sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store)
|
sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store)
|
||||||
|
|
||||||
sub_assembly_items_store.sort(key=lambda d: d.bom_level, reverse=True) # sort by bom level
|
|
||||||
|
|
||||||
for idx, row in enumerate(sub_assembly_items_store):
|
for idx, row in enumerate(sub_assembly_items_store):
|
||||||
row.idx = idx + 1
|
row.idx = idx + 1
|
||||||
self.append("sub_assembly_items", row)
|
self.append("sub_assembly_items", row)
|
||||||
|
|||||||
@@ -653,49 +653,6 @@ class TestProductionPlan(FrappeTestCase):
|
|||||||
|
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
|
|
||||||
def test_subassmebly_sorting(self):
|
|
||||||
"Test subassembly sorting in case of multiple items with nested BOMs."
|
|
||||||
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
|
|
||||||
|
|
||||||
prefix = "_TestLevel_"
|
|
||||||
boms = {
|
|
||||||
"Assembly": {
|
|
||||||
"SubAssembly1": {
|
|
||||||
"ChildPart1": {},
|
|
||||||
"ChildPart2": {},
|
|
||||||
},
|
|
||||||
"ChildPart6": {},
|
|
||||||
"SubAssembly4": {"SubSubAssy2": {"ChildPart7": {}}},
|
|
||||||
},
|
|
||||||
"MegaDeepAssy": {
|
|
||||||
"SecretSubassy": {
|
|
||||||
"SecretPart": {"VerySecret": {"SuperSecret": {"Classified": {}}}},
|
|
||||||
},
|
|
||||||
# ^ assert that this is
|
|
||||||
# first item in subassy table
|
|
||||||
},
|
|
||||||
}
|
|
||||||
create_nested_bom(boms, prefix=prefix)
|
|
||||||
|
|
||||||
items = [prefix + item_code for item_code in boms.keys()]
|
|
||||||
plan = create_production_plan(item_code=items[0], do_not_save=True)
|
|
||||||
plan.append(
|
|
||||||
"po_items",
|
|
||||||
{
|
|
||||||
"use_multi_level_bom": 1,
|
|
||||||
"item_code": items[1],
|
|
||||||
"bom_no": frappe.db.get_value("Item", items[1], "default_bom"),
|
|
||||||
"planned_qty": 1,
|
|
||||||
"planned_start_date": now_datetime(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
plan.get_sub_assembly_items()
|
|
||||||
|
|
||||||
bom_level_order = [d.bom_level for d in plan.sub_assembly_items]
|
|
||||||
self.assertEqual(bom_level_order, sorted(bom_level_order, reverse=True))
|
|
||||||
# lowest most level of subassembly should be first
|
|
||||||
self.assertIn("SuperSecret", plan.sub_assembly_items[0].production_item)
|
|
||||||
|
|
||||||
def test_multiple_work_order_for_production_plan_item(self):
|
def test_multiple_work_order_for_production_plan_item(self):
|
||||||
"Test producing Prod Plan (making WO) in parts."
|
"Test producing Prod Plan (making WO) in parts."
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ frappe.query_reports["Production Plan Summary"] = {
|
|||||||
"formatter": function(value, row, column, data, default_formatter) {
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
value = default_formatter(value, row, column, data);
|
value = default_formatter(value, row, column, data);
|
||||||
|
|
||||||
if (column.fieldname == "document_name") {
|
if (column.fieldname == "item_code") {
|
||||||
var color = data.pending_qty > 0 ? 'red': 'green';
|
var color = data.pending_qty > 0 ? 'red': 'green';
|
||||||
value = `<a style='color:${color}' href="#Form/${data['document_type']}/${data['document_name']}" data-doctype="${data['document_type']}">${data['document_name']}</a>`;
|
value = `<a style='color:${color}' href="/app/item/${data['item_code']}" data-doctype="Item">${data['item_code']}</a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ def get_production_plan_item_details(filters, data, order_details):
|
|||||||
{
|
{
|
||||||
"indent": 0,
|
"indent": 0,
|
||||||
"item_code": row.item_code,
|
"item_code": row.item_code,
|
||||||
|
"sales_order": row.get("sales_order"),
|
||||||
"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
|
"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
|
||||||
"qty": row.planned_qty,
|
"qty": row.planned_qty,
|
||||||
"document_type": "Work Order",
|
"document_type": "Work Order",
|
||||||
@@ -80,7 +81,7 @@ def get_production_plan_sub_assembly_item_details(
|
|||||||
|
|
||||||
data.append(
|
data.append(
|
||||||
{
|
{
|
||||||
"indent": 1,
|
"indent": 1 + item.indent,
|
||||||
"item_code": item.production_item,
|
"item_code": item.production_item,
|
||||||
"item_name": item.item_name,
|
"item_name": item.item_name,
|
||||||
"qty": item.qty,
|
"qty": item.qty,
|
||||||
@@ -98,7 +99,7 @@ def get_work_order_details(filters, order_details):
|
|||||||
for row in frappe.get_all(
|
for row in frappe.get_all(
|
||||||
"Work Order",
|
"Work Order",
|
||||||
filters={"production_plan": filters.get("production_plan")},
|
filters={"production_plan": filters.get("production_plan")},
|
||||||
fields=["name", "produced_qty", "production_plan", "production_item"],
|
fields=["name", "produced_qty", "production_plan", "production_item", "sales_order"],
|
||||||
):
|
):
|
||||||
order_details.setdefault((row.name, row.production_item), row)
|
order_details.setdefault((row.name, row.production_item), row)
|
||||||
|
|
||||||
@@ -118,10 +119,17 @@ def get_column(filters):
|
|||||||
"label": _("Finished Good"),
|
"label": _("Finished Good"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"width": 300,
|
"width": 240,
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
},
|
},
|
||||||
{"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100},
|
{"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 150},
|
||||||
|
{
|
||||||
|
"label": _("Sales Order"),
|
||||||
|
"options": "Sales Order",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"fieldname": "sales_order",
|
||||||
|
"width": 100,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": _("Document Type"),
|
"label": _("Document Type"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@@ -133,10 +141,16 @@ def get_column(filters):
|
|||||||
"label": _("Document Name"),
|
"label": _("Document Name"),
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
"fieldname": "document_name",
|
"fieldname": "document_name",
|
||||||
"width": 150,
|
"options": "document_type",
|
||||||
|
"width": 180,
|
||||||
},
|
},
|
||||||
{"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
|
{"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
|
||||||
{"label": _("Order Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 120},
|
{"label": _("Order Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 120},
|
||||||
{"label": _("Received Qty"), "fieldtype": "Float", "fieldname": "produced_qty", "width": 160},
|
{
|
||||||
|
"label": _("Produced / Received Qty"),
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"fieldname": "produced_qty",
|
||||||
|
"width": 200,
|
||||||
|
},
|
||||||
{"label": _("Pending Qty"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 110},
|
{"label": _("Pending Qty"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 110},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -541,6 +541,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
|
|||||||
primary_action={
|
primary_action={
|
||||||
"label": "Send Email",
|
"label": "Send Email",
|
||||||
"server_action": "erpnext.selling.doctype.customer.customer.send_emails",
|
"server_action": "erpnext.selling.doctype.customer.customer.send_emails",
|
||||||
|
"hide_on_success": True,
|
||||||
"args": {
|
"args": {
|
||||||
"customer": customer,
|
"customer": customer,
|
||||||
"customer_outstanding": customer_outstanding,
|
"customer_outstanding": customer_outstanding,
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ def get_data(filters=None):
|
|||||||
|
|
||||||
territory_orders = []
|
territory_orders = []
|
||||||
if t_quotation_names and sales_orders:
|
if t_quotation_names and sales_orders:
|
||||||
list(filter(lambda x: x.quotation in t_quotation_names, sales_orders))
|
territory_orders = list(filter(lambda x: x.quotation in t_quotation_names, sales_orders))
|
||||||
t_order_names = []
|
t_order_names = []
|
||||||
if territory_orders:
|
if territory_orders:
|
||||||
t_order_names = [t.name for t in territory_orders]
|
t_order_names = [t.name for t in territory_orders]
|
||||||
|
|||||||
@@ -108,7 +108,16 @@ class TransactionDeletionRecord(Document):
|
|||||||
|
|
||||||
if no_of_docs > 0:
|
if no_of_docs > 0:
|
||||||
self.delete_version_log(docfield["parent"], docfield["fieldname"])
|
self.delete_version_log(docfield["parent"], docfield["fieldname"])
|
||||||
self.delete_communications(docfield["parent"], docfield["fieldname"])
|
|
||||||
|
reference_docs = frappe.get_all(
|
||||||
|
docfield["parent"], filters={docfield["fieldname"]: self.company}
|
||||||
|
)
|
||||||
|
reference_doc_names = [r.name for r in reference_docs]
|
||||||
|
|
||||||
|
self.delete_communications(docfield["parent"], reference_doc_names)
|
||||||
|
self.delete_comments(docfield["parent"], reference_doc_names)
|
||||||
|
self.unlink_attachments(docfield["parent"], reference_doc_names)
|
||||||
|
|
||||||
self.populate_doctypes_table(tables, docfield["parent"], no_of_docs)
|
self.populate_doctypes_table(tables, docfield["parent"], no_of_docs)
|
||||||
|
|
||||||
self.delete_child_tables(docfield["parent"], docfield["fieldname"])
|
self.delete_child_tables(docfield["parent"], docfield["fieldname"])
|
||||||
@@ -197,19 +206,49 @@ class TransactionDeletionRecord(Document):
|
|||||||
(versions.ref_doctype == doctype) & (versions.docname.isin(batch))
|
(versions.ref_doctype == doctype) & (versions.docname.isin(batch))
|
||||||
).run()
|
).run()
|
||||||
|
|
||||||
def delete_communications(self, doctype, company_fieldname):
|
def delete_communications(self, doctype, reference_doc_names):
|
||||||
reference_docs = frappe.get_all(doctype, filters={company_fieldname: self.company})
|
|
||||||
reference_doc_names = [r.name for r in reference_docs]
|
|
||||||
|
|
||||||
communications = frappe.get_all(
|
communications = frappe.get_all(
|
||||||
"Communication",
|
"Communication",
|
||||||
filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]},
|
filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]},
|
||||||
)
|
)
|
||||||
communication_names = [c.name for c in communications]
|
communication_names = [c.name for c in communications]
|
||||||
|
|
||||||
|
if not communication_names:
|
||||||
|
return
|
||||||
|
|
||||||
for batch in create_batch(communication_names, self.batch_size):
|
for batch in create_batch(communication_names, self.batch_size):
|
||||||
frappe.delete_doc("Communication", batch, ignore_permissions=True)
|
frappe.delete_doc("Communication", batch, ignore_permissions=True)
|
||||||
|
|
||||||
|
def delete_comments(self, doctype, reference_doc_names):
|
||||||
|
comments = frappe.get_all(
|
||||||
|
"Comment",
|
||||||
|
filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]},
|
||||||
|
)
|
||||||
|
comment_names = [c.name for c in comments]
|
||||||
|
|
||||||
|
if not comment_names:
|
||||||
|
return
|
||||||
|
|
||||||
|
for batch in create_batch(comment_names, self.batch_size):
|
||||||
|
frappe.delete_doc("Comment", batch, ignore_permissions=True)
|
||||||
|
|
||||||
|
def unlink_attachments(self, doctype, reference_doc_names):
|
||||||
|
files = frappe.get_all(
|
||||||
|
"File",
|
||||||
|
filters={"attached_to_doctype": doctype, "attached_to_name": ["in", reference_doc_names]},
|
||||||
|
)
|
||||||
|
file_names = [c.name for c in files]
|
||||||
|
|
||||||
|
if not file_names:
|
||||||
|
return
|
||||||
|
|
||||||
|
file = qb.DocType("File")
|
||||||
|
|
||||||
|
for batch in create_batch(file_names, self.batch_size):
|
||||||
|
qb.update(file).set(file.attached_to_doctype, None).set(file.attached_to_name, None).where(
|
||||||
|
file.name.isin(batch)
|
||||||
|
).run()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_doctypes_to_be_ignored():
|
def get_doctypes_to_be_ignored():
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
"oldfieldname": "item",
|
"oldfieldname": "item",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
|
"read_only_depends_on": "eval:!doc.__islocal",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -207,7 +208,7 @@
|
|||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"links": [],
|
"links": [],
|
||||||
"max_attachments": 5,
|
"max_attachments": 5,
|
||||||
"modified": "2022-02-21 08:08:23.999236",
|
"modified": "2023-11-09 12:17:28.339975",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Batch",
|
"name": "Batch",
|
||||||
|
|||||||
@@ -3671,7 +3671,7 @@ Cannot Optimize Route as Driver Address is Missing.,"Route kann nicht optimiert
|
|||||||
Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Aufgabe {0} kann nicht abgeschlossen werden, da die abhängige Aufgabe {1} nicht abgeschlossen / abgebrochen wurde.",
|
Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Aufgabe {0} kann nicht abgeschlossen werden, da die abhängige Aufgabe {1} nicht abgeschlossen / abgebrochen wurde.",
|
||||||
Cannot create loan until application is approved,"Darlehen kann erst erstellt werden, wenn der Antrag genehmigt wurde",
|
Cannot create loan until application is approved,"Darlehen kann erst erstellt werden, wenn der Antrag genehmigt wurde",
|
||||||
Cannot find a matching Item. Please select some other value for {0}.,Ein passender Artikel kann nicht gefunden werden. Bitte einen anderen Wert für {0} auswählen.,
|
Cannot find a matching Item. Please select some other value for {0}.,Ein passender Artikel kann nicht gefunden werden. Bitte einen anderen Wert für {0} auswählen.,
|
||||||
"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Kontoeinstellungen fest",
|
"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Für Artikel {0} in Zeile {1} kann nicht mehr als {2} zusätzlich in Rechnung gestellt werden. Um diese Überfakturierung zuzulassen, passen Sie bitte die Grenzwerte in den Buchhaltungseinstellungen an.",
|
||||||
"Capacity Planning Error, planned start time can not be same as end time","Kapazitätsplanungsfehler, die geplante Startzeit darf nicht mit der Endzeit übereinstimmen",
|
"Capacity Planning Error, planned start time can not be same as end time","Kapazitätsplanungsfehler, die geplante Startzeit darf nicht mit der Endzeit übereinstimmen",
|
||||||
Categories,Kategorien,
|
Categories,Kategorien,
|
||||||
Changes in {0},Änderungen in {0},
|
Changes in {0},Änderungen in {0},
|
||||||
@@ -4125,7 +4125,7 @@ This page keeps track of your items in which buyers have showed some interest.,"
|
|||||||
Thursday,Donnerstag,
|
Thursday,Donnerstag,
|
||||||
Timing,Zeitliche Koordinierung,
|
Timing,Zeitliche Koordinierung,
|
||||||
Title,Bezeichnung,
|
Title,Bezeichnung,
|
||||||
"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Kontoeinstellungen oder im Artikel, um eine Überberechnung zuzulassen.",
|
"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Buchhaltungseinstellungen oder im Artikel, um eine Überberechnung zuzulassen.",
|
||||||
"To allow over receipt / delivery, update ""Over Receipt/Delivery Allowance"" in Stock Settings or the Item.","Um eine Überbestätigung / Überlieferung zu ermöglichen, aktualisieren Sie "Überbestätigung / Überlieferung" in den Lagereinstellungen oder im Artikel.",
|
"To allow over receipt / delivery, update ""Over Receipt/Delivery Allowance"" in Stock Settings or the Item.","Um eine Überbestätigung / Überlieferung zu ermöglichen, aktualisieren Sie "Überbestätigung / Überlieferung" in den Lagereinstellungen oder im Artikel.",
|
||||||
To date needs to be before from date,Das Datum muss vor dem Datum liegen,
|
To date needs to be before from date,Das Datum muss vor dem Datum liegen,
|
||||||
Total,Summe,
|
Total,Summe,
|
||||||
@@ -4533,8 +4533,8 @@ Mandatory For Profit and Loss Account,Obligatorisch für Gewinn- und Verlustrech
|
|||||||
Accounting Period,Abrechnungszeitraum,
|
Accounting Period,Abrechnungszeitraum,
|
||||||
Period Name,Zeitraumname,
|
Period Name,Zeitraumname,
|
||||||
Closed Documents,Geschlossene Dokumente,
|
Closed Documents,Geschlossene Dokumente,
|
||||||
Accounts Settings,Konteneinstellungen,
|
Accounts Settings,Buchhaltungseinstellungen,
|
||||||
Settings for Accounts,Konteneinstellungen,
|
Settings for Accounts,Einstellungen für die Buchhaltung,
|
||||||
Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen,
|
Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen,
|
||||||
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Benutzer mit dieser Rolle sind berechtigt Konten zu sperren und Buchungen zu gesperrten Konten zu erstellen/verändern,
|
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Benutzer mit dieser Rolle sind berechtigt Konten zu sperren und Buchungen zu gesperrten Konten zu erstellen/verändern,
|
||||||
Determine Address Tax Category From,Adresssteuerkategorie bestimmen von,
|
Determine Address Tax Category From,Adresssteuerkategorie bestimmen von,
|
||||||
@@ -8622,7 +8622,7 @@ Template Title,Vorlagentitel,
|
|||||||
Journal Entry Type,Buchungssatz-Typ,
|
Journal Entry Type,Buchungssatz-Typ,
|
||||||
Journal Entry Template Account,Buchungssatzvorlagenkonto,
|
Journal Entry Template Account,Buchungssatzvorlagenkonto,
|
||||||
Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten,
|
Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten,
|
||||||
Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Konteneinstellungen und versuchen Sie es erneut,
|
Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Buchhaltungseinstellungen und versuchen Sie es erneut,
|
||||||
End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen,
|
End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen,
|
||||||
Total Counts Targeted,Gesamtzahl der anvisierten Zählungen,
|
Total Counts Targeted,Gesamtzahl der anvisierten Zählungen,
|
||||||
Total Counts Completed,Gesamtzahl der abgeschlossenen Zählungen,
|
Total Counts Completed,Gesamtzahl der abgeschlossenen Zählungen,
|
||||||
|
|||||||
|
Can't render this file because it is too large.
|
Reference in New Issue
Block a user