Merge branch 'develop' into print-language

This commit is contained in:
barredterra
2024-05-27 18:52:05 +02:00
26 changed files with 5866 additions and 5019 deletions

View File

@@ -82,7 +82,7 @@
"icon": "fa fa-calendar",
"idx": 1,
"links": [],
"modified": "2024-01-30 12:35:38.645968",
"modified": "2024-05-27 17:29:55.560840",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Fiscal Year",
@@ -127,6 +127,10 @@
{
"read": 1,
"role": "Stock Manager"
},
{
"read": 1,
"role": "Auditor"
}
],
"show_name_in_global_search": 1,

View File

@@ -481,7 +481,7 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
if closing_entry:
closing_entry.set_status(update=True, status="Failed")
if isinstance(error_message, list):
error_message = frappe.json.dumps(error_message)
error_message = json.dumps(error_message)
closing_entry.db_set("error_message", error_message)
raise

View File

@@ -1,6 +1,8 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
import frappe
from frappe import _, qb
from frappe.model.document import Document
@@ -502,7 +504,7 @@ def is_any_doc_running(for_filter: str | dict | None = None) -> str | None:
running_doc = None
if for_filter:
if isinstance(for_filter, str):
for_filter = frappe.json.loads(for_filter)
for_filter = json.loads(for_filter)
running_doc = frappe.db.get_value(
"Process Payment Reconciliation",

View File

@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
import copy
import json
import frappe
from frappe.model.dynamic_links import get_dynamic_link_map
@@ -3733,9 +3734,9 @@ class TestSalesInvoice(FrappeTestCase):
map_docs(
method="erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice",
source_names=frappe.json.dumps([dn1.name, dn2.name]),
source_names=json.dumps([dn1.name, dn2.name]),
target_doc=si,
args=frappe.json.dumps({"customer": dn1.customer, "merge_taxes": 1, "filtered_children": []}),
args=json.dumps({"customer": dn1.customer, "merge_taxes": 1, "filtered_children": []}),
)
si.save().submit()

View File

@@ -870,7 +870,8 @@
"label": "Purchase Order",
"options": "Purchase Order",
"print_hide": 1,
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "column_break_92",
@@ -926,7 +927,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-03-27 13:10:36.139679",
"modified": "2024-05-23 16:36:18.970862",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -1,6 +1,8 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
import frappe
from frappe import _, qb
from frappe.model.document import Document
@@ -163,7 +165,7 @@ def get_linked_payments_for_doc(
@frappe.whitelist()
def create_unreconcile_doc_for_selection(selections=None):
if selections:
selections = frappe.json.loads(selections)
selections = json.loads(selections)
# assuming each row is a unique voucher
for row in selections:
unrecon = frappe.new_doc("Unreconcile Payment")

View File

@@ -56,7 +56,7 @@ def get_fiscal_year(
date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False, boolean=False
):
if isinstance(boolean, str):
boolean = frappe.json.loads(boolean)
boolean = loads(boolean)
fiscal_years = get_fiscal_years(
date, fiscal_year, label, verbose, company, as_dict=as_dict, boolean=boolean
@@ -1109,7 +1109,7 @@ def get_companies():
@frappe.whitelist()
def get_children(doctype, parent, company, is_root=False, include_disabled=False):
if isinstance(include_disabled, str):
include_disabled = frappe.json.loads(include_disabled)
include_disabled = loads(include_disabled)
from erpnext.accounts.report.financial_statements import sort_accounts
parent_fieldname = "parent_" + doctype.lower().replace(" ", "_")

View File

@@ -513,7 +513,7 @@ erpnext.buying.RequestforQuotationController = class RequestforQuotationControll
method: "frappe.desk.doctype.tag.tag.get_tagged_docs",
args: {
doctype: "Supplier",
tag: args.tag,
tag: "%" + args.tag + "%",
},
callback: load_suppliers,
});

View File

@@ -1942,7 +1942,7 @@ class AccountsController(TransactionBase):
def set_advance_payment_status(self):
new_status = None
stati = frappe.get_list(
stati = frappe.get_all(
"Payment Request",
{
"reference_doctype": self.doctype,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -55,6 +55,14 @@ frappe.ui.form.on("Project", {
filters: filters,
};
});
frm.set_query("cost_center", () => {
return {
filters: {
company: frm.doc.company,
},
};
});
},
refresh: function (frm) {

View File

@@ -2,6 +2,8 @@
# License: GNU General Public License v3. See license.txt
import json
import frappe
from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
@@ -321,7 +323,7 @@ class TestCustomer(FrappeTestCase):
frappe.ValidationError,
update_child_qty_rate,
so.doctype,
frappe.json.dumps([modified_item]),
json.dumps([modified_item]),
so.name,
)

View File

@@ -770,13 +770,11 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
flt(doc.per_billed, precision("per_billed", doc)) <
100 + frappe.boot.sysdefaults.over_billing_allowance
) {
if (frappe.model.can_create("Payment Request")) {
this.frm.add_custom_button(
__("Payment Request"),
() => this.make_payment_request(),
__("Create")
);
}
this.frm.add_custom_button(
__("Payment Request"),
() => this.make_payment_request(),
__("Create")
);
if (frappe.model.can_create("Payment Entry")) {
this.frm.add_custom_button(

View File

@@ -1140,7 +1140,8 @@
"hide_seconds": 1,
"label": "Inter Company Order Reference",
"options": "Purchase Order",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "project",
@@ -1661,7 +1662,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2024-04-20 01:20:11.190908",
"modified": "2024-05-27 18:51:54.905804",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",

View File

@@ -21,9 +21,17 @@ frappe.query_reports["Sales Analytics"] = {
},
{
fieldname: "doc_type",
label: __("based_on"),
label: __("Based On"),
fieldtype: "Select",
options: ["Sales Order", "Delivery Note", "Sales Invoice"],
options: [
"All",
"Quotation",
"Sales Order",
"Delivery Note",
"Sales Invoice",
"Sales Invoice (due)",
"Payment Entry",
],
default: "Sales Invoice",
reqd: 1,
},
@@ -42,14 +50,18 @@ frappe.query_reports["Sales Analytics"] = {
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
default:
frappe.defaults.get_user_default("sales_start_date") ||
erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
reqd: 1,
},
{
fieldname: "to_date",
label: __("To Date"),
fieldtype: "Date",
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
default:
frappe.defaults.get_user_default("sales_end_date") ||
erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
reqd: 1,
},
{
@@ -73,6 +85,19 @@ frappe.query_reports["Sales Analytics"] = {
default: "Monthly",
reqd: 1,
},
{
fieldname: "curves",
label: __("Curves"),
fieldtype: "Select",
options: [
{ value: "select", label: __("Select") },
{ value: "all", label: __("All") },
{ value: "non-zeros", label: __("Non-Zeros") },
{ value: "total", label: __("Total Only") },
],
default: "select",
reqd: 1,
},
],
get_datatable_options(options) {
return Object.assign(options, {

View File

@@ -10,17 +10,58 @@ from erpnext.accounts.utils import get_fiscal_year
def execute(filters=None):
return Analytics(filters).run()
filters = frappe._dict(filters or {})
# Special report showing all doctype totals on a single chart; overrides some filters
if filters.doc_type == "All":
filters.tree_type = "Customer"
filters.value_quantity = "Value"
filters.curves = "total"
output = None
for dt in [
"Quotation",
"Sales Order",
"Delivery Note",
"Sales Invoice",
"Sales Invoice (due)",
"Payment Entry",
]:
filters.doc_type = dt
output = append_report(dt, output, Analytics(filters).run())
return output
else:
return Analytics(filters).run()
def append_report(dt, org, new):
# idx 1 is data, 3 is chart
new[1].insert(0, {"entity": dt}) # heading
new[1].append({}) # empty row
# datasets can be an empty list if no dates are supplied by the Dashboard Chart
if not new[3]["data"]["datasets"]:
new[3]["data"]["datasets"].append({"name": None, "values": []})
new[3]["data"]["datasets"][0]["name"] = dt # override curve name
if org:
org[1].extend(new[1])
org[3]["data"]["datasets"].extend(new[3]["data"]["datasets"])
return org
else:
return new
class Analytics:
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
if self.filters.doc_type == "Payment Entry" and self.filters.value_quantity == "Quantity":
frappe.throw(_("Only Value available for Payment Entry"))
self.date_field = (
"transaction_date"
if self.filters.doc_type in ["Sales Order", "Purchase Order"]
if self.filters.doc_type in ["Quotation", "Sales Order", "Purchase Order"]
else "due_date"
if self.filters.doc_type == "Sales Invoice (due)"
else "posting_date"
)
if self.filters.doc_type.startswith("Sales Invoice"):
self.filters.doc_type = "Sales Invoice"
self.months = [
"Jan",
"Feb",
@@ -95,25 +136,37 @@ class Analytics:
self.get_rows()
elif self.filters.tree_type == "Item":
if self.filters.doc_type == "Payment Entry":
self.data = []
return
self.get_sales_transactions_based_on_items()
self.get_rows()
elif self.filters.tree_type in ["Customer Group", "Supplier Group", "Territory"]:
if self.filters.doc_type == "Payment Entry":
self.data = []
return
self.get_sales_transactions_based_on_customer_or_territory_group()
self.get_rows_by_group()
elif self.filters.tree_type == "Item Group":
if self.filters.doc_type == "Payment Entry":
self.data = []
return
self.get_sales_transactions_based_on_item_group()
self.get_rows_by_group()
elif self.filters.tree_type == "Order Type":
if self.filters.doc_type != "Sales Order":
if self.filters.doc_type not in ["Quotation", "Sales Order"]:
self.data = []
return
self.get_sales_transactions_based_on_order_type()
self.get_rows_by_group()
elif self.filters.tree_type == "Project":
if self.filters.doc_type == "Quotation":
self.data = []
return
self.get_sales_transactions_based_on_project()
self.get_rows()
@@ -141,11 +194,22 @@ class Analytics:
value_field = "total_qty as value_field"
if self.filters.tree_type == "Customer":
entity = "customer as entity"
entity_name = "customer_name as entity_name"
if self.filters.doc_type == "Quotation":
entity = "party_name as entity"
elif self.filters.doc_type == "Payment Entry":
entity = "party as entity"
entity_name = "party_name as entity_name"
value_field = "base_received_amount as value_field"
else:
entity = "customer as entity"
else:
entity = "supplier as entity"
entity_name = "supplier_name as entity_name"
if self.filters.doc_type == "Payment Entry":
entity = "party as entity"
entity_name = "party_name as entity_name"
value_field = "base_paid_amount as value_field"
self.entries = frappe.get_all(
self.filters.doc_type,
@@ -232,6 +296,9 @@ class Analytics:
else:
value_field = "total_qty as value_field"
if self.filters.doc_type == "Payment Entry":
value_field = "base_received_amount as value_field"
entity = "project as entity"
self.entries = frappe.get_all(
@@ -401,7 +468,33 @@ class Analytics:
labels = [d.get("label") for d in self.columns[3 : length - 1]]
else:
labels = [d.get("label") for d in self.columns[1 : length - 1]]
self.chart = {"data": {"labels": labels, "datasets": []}, "type": "line"}
datasets = []
if self.filters.curves != "select":
for curve in self.data:
data = {
"name": curve.get("entity_name", curve["entity"]),
"values": [curve[scrub(label)] for label in labels],
}
if self.filters.curves == "non-zeros" and not sum(data["values"]):
continue
elif self.filters.curves == "total" and "indent" in curve:
if curve["indent"] == 0:
datasets.append(data)
elif self.filters.curves == "total":
if datasets:
a = [
data["values"][idx] + datasets[0]["values"][idx]
for idx in range(len(data["values"]))
]
datasets[0]["values"] = a
else:
datasets.append(data)
datasets[0]["name"] = _("Total")
else:
datasets.append(data)
self.chart = {"data": {"labels": labels, "datasets": datasets}, "type": "line"}
if self.filters["value_quantity"] == "Value":
self.chart["fieldtype"] = "Currency"

View File

@@ -779,7 +779,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
"modified": "2024-05-16 12:39:54.694232",
"modified": "2024-05-27 17:32:49.057386",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
@@ -835,6 +835,10 @@
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"role": "Auditor",
"select": 1
}
],
"show_name_in_global_search": 1,

View File

@@ -2,6 +2,8 @@
# License: GNU General Public License v3. See license.txt
import json
import frappe
from frappe.utils.nestedset import NestedSet, get_root_of
@@ -71,7 +73,7 @@ def get_abbreviated_name(name, company):
@frappe.whitelist()
def get_children(doctype, parent=None, company=None, is_root=False, include_disabled=False):
if isinstance(include_disabled, str):
include_disabled = frappe.json.loads(include_disabled)
include_disabled = json.loads(include_disabled)
fields = ["name as value", "is_group as expandable"]
filters = {}

View File

@@ -2,6 +2,8 @@
# License: GNU General Public License v3. See license.txt
import json
import frappe
from frappe import _, throw
from frappe.contacts.address_and_contact import load_address_and_contact
@@ -186,7 +188,7 @@ def get_children(doctype, parent=None, company=None, is_root=False, include_disa
parent = ""
if isinstance(include_disabled, str):
include_disabled = frappe.json.loads(include_disabled)
include_disabled = json.loads(include_disabled)
fields = ["name as value", "is_group as expandable"]
filters = [