Merge pull request #33164 from frappe/version-13-hotfix

chore: release v13
This commit is contained in:
Deepesh Garg
2022-11-29 18:47:36 +05:30
committed by GitHub
17 changed files with 127 additions and 38 deletions

View File

@@ -298,20 +298,22 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no
)
round_off_account_exists = False
round_off_gle = frappe._dict()
for d in gl_map:
if d.account == round_off_account:
round_off_gle = d
if d.debit:
debit_credit_diff -= flt(d.debit)
else:
debit_credit_diff += flt(d.credit)
round_off_account_exists = True
round_off_account_exists = False
if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)):
gl_map.remove(round_off_gle)
return
if gl_map[0].voucher_type != "Period Closing Voucher":
for d in gl_map:
if d.account == round_off_account:
round_off_gle = d
if d.debit:
debit_credit_diff -= flt(d.debit) - flt(d.credit)
else:
debit_credit_diff += flt(d.credit)
round_off_account_exists = True
if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)):
gl_map.remove(round_off_gle)
return
if not round_off_gle:
for k in ["voucher_type", "voucher_no", "company", "posting_date", "remarks"]:
@@ -334,7 +336,6 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
)
update_accounting_dimensions(round_off_gle)
if not round_off_account_exists:
gl_map.append(round_off_gle)

View File

@@ -685,10 +685,10 @@ class ReceivablePayableReport(object):
if self.filters.get(scrub(self.party_type)):
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
doc_currency_fields = "debit as debit_in_account_currency, credit as credit_in_account_currency"
else:
select_fields = "debit, credit"
doc_currency_fields = "debit_in_account_currency, credit_in_account_currency"
doc_currency_fields = "debit_in_account_currency, credit_in_account_currency"
remarks = ", remarks" if self.filters.get("show_remarks") else ""

View File

@@ -825,7 +825,9 @@ def update_maintenance_status():
for asset in assets:
asset = frappe.get_doc("Asset", asset.name)
if frappe.db.exists("Asset Repair", {"asset_name": asset.name, "repair_status": "Pending"}):
if frappe.db.exists(
"Asset Repair", {"asset_name": asset.name, "repair_status": "Pending", "docstatus": 0}
):
asset.set_status("Out of Order")
elif frappe.db.exists(
"Asset Maintenance Task", {"parent": asset.name, "next_due_date": today()}

View File

@@ -578,6 +578,7 @@ class SellingController(StockController):
"customer_address": "address_display",
"shipping_address_name": "shipping_address",
"company_address": "company_address_display",
"dispatch_address_name": "dispatch_address",
}
for address_field, address_display_field in address_dict.items():

View File

@@ -191,7 +191,9 @@ def get_total_pledged_security_value(loan):
for security, qty in pledged_securities.items():
after_haircut_percentage = 100 - hair_cut_map.get(security)
security_value += (loan_security_price_map.get(security) * qty * after_haircut_percentage) / 100
security_value += (
loan_security_price_map.get(security, 0) * qty * after_haircut_percentage
) / 100
return security_value

View File

@@ -54,11 +54,11 @@ frappe.query_reports["Job Card Summary"] = {
options: ["", "Open", "Work In Progress", "Completed", "On Hold"]
},
{
label: __("Sales Orders"),
fieldname: "sales_order",
label: __("Work Orders"),
fieldname: "work_order",
fieldtype: "MultiSelectList",
get_data: function(txt) {
return frappe.db.get_link_options('Sales Order', txt);
return frappe.db.get_link_options('Work Order', txt);
}
},
{

View File

@@ -36,10 +36,14 @@ def get_data(filters):
"total_time_in_mins",
]
for field in ["work_order", "workstation", "operation", "company"]:
for field in ["work_order", "production_item"]:
if filters.get(field):
query_filters[field] = ("in", filters.get(field))
for field in ["workstation", "operation", "status", "company"]:
if filters.get(field):
query_filters[field] = filters.get(field)
data = frappe.get_all("Job Card", fields=fields, filters=query_filters)
if not data:

View File

@@ -39,10 +39,14 @@ def get_data(filters):
"lead_time",
]
for field in ["sales_order", "production_item", "status", "company"]:
for field in ["sales_order", "production_item"]:
if filters.get(field):
query_filters[field] = ("in", filters.get(field))
for field in ["status", "company"]:
if filters.get(field):
query_filters[field] = filters.get(field)
query_filters["planned_start_date"] = (">=", filters.get("from_date"))
query_filters["planned_end_date"] = ("<=", filters.get("to_date"))

View File

@@ -346,6 +346,8 @@ class PayrollEntry(Document):
"credit_in_account_currency": flt(payable_amt, precision),
"exchange_rate": flt(exchange_rate),
"cost_center": self.cost_center,
"reference_type": self.doctype,
"reference_name": self.name,
},
accounting_dimensions,
)
@@ -720,12 +722,21 @@ def get_month_details(year, month):
def get_payroll_entry_bank_entries(payroll_entry_name):
journal_entries = frappe.db.sql(
"select name from `tabJournal Entry Account` "
'where reference_type="Payroll Entry" '
"and reference_name=%s and docstatus=1",
"""
select
je.name
from
`tabJournal Entry` je,
`tabJournal Entry Account` jea
where
je.name = jea.parent
and je.voucher_type = 'Bank Entry'
and jea.reference_type = 'Payroll Entry'
and jea.reference_name = %s
""",
payroll_entry_name,
as_dict=1,
)
as_dict=True,
) # nosemgrep
return journal_entries

View File

@@ -133,9 +133,17 @@ class TestPayrollEntry(FrappeTestCase):
payment_entry = frappe.db.sql(
"""
Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea
Where je.name = jea.parent
And jea.reference_name = %s
select
ifnull(sum(je.total_debit),0) as total_debit,
ifnull(sum(je.total_credit),0) as total_credit
from
`tabJournal Entry` je,
`tabJournal Entry Account` jea
Where
je.name = jea.parent
and je.voucher_type = 'Bank Entry'
and jea.reference_type = 'Payroll Entry'
and jea.reference_name = %s
""",
(payroll_entry.name),
as_dict=1,

View File

@@ -10,3 +10,8 @@ class EInvoiceSettings(Document):
def validate(self):
if self.enable and not self.credentials:
frappe.throw(_("You must add atleast one credentials to be able to use E Invoicing."))
prev_doc = self.get_doc_before_save()
if prev_doc.client_secret != self.client_secret or prev_doc.client_id != self.client_id:
self.auth_token = None
self.token_expiry = None

View File

@@ -370,9 +370,6 @@ frappe.ui.form.on("Material Request Item", {
if (flt(d.qty) < flt(d.min_order_qty)) {
frappe.msgprint(__("Warning: Material Requested Qty is less than Minimum Order Qty"));
}
const item = locals[doctype][name];
frm.events.get_item_data(frm, item, false);
},
from_warehouse: function(frm, doctype, name) {

View File

@@ -34,6 +34,22 @@ frappe.ui.form.on('Repost Item Valuation', {
frm.trigger('setup_realtime_progress');
},
based_on: function(frm) {
var fields_to_reset = [];
if (frm.doc.based_on == 'Transaction') {
fields_to_reset = ['item_code', 'warehouse'];
} else if (frm.doc.based_on == 'Item and Warehouse') {
fields_to_reset = ['voucher_type', 'voucher_no'];
}
if (fields_to_reset) {
fields_to_reset.forEach(field => {
frm.set_value(field, undefined);
});
}
},
setup_realtime_progress: function(frm) {
frappe.realtime.on('item_reposting_progress', data => {
if (frm.doc.name !== data.name) {

View File

@@ -50,13 +50,15 @@
"fieldname": "posting_date",
"fieldtype": "Date",
"label": "Posting Date",
"read_only_depends_on": "eval: doc.based_on == \"Transaction\"",
"reqd": 1
},
{
"fetch_from": "voucher_no.posting_time",
"fieldname": "posting_time",
"fieldtype": "Time",
"label": "Posting Time"
"label": "Posting Time",
"read_only_depends_on": "eval: doc.based_on == \"Transaction\""
},
{
"default": "Queued",
@@ -195,7 +197,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2022-06-13 12:20:22.182322",
"modified": "2022-11-28 16:00:05.637440",
"modified_by": "Administrator",
"module": "Stock",
"name": "Repost Item Valuation",

View File

@@ -1050,7 +1050,8 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => {
if (frm.doc.purpose === 'Material Receipt') return;
frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() {
new erpnext.SerialNoBatchSelector({
if (frm.batch_selector?.dialog?.display) return;
frm.batch_selector = new erpnext.SerialNoBatchSelector({
frm: frm,
item: item,
warehouse_details: get_warehouse_type_and_name(item),

View File

@@ -229,7 +229,7 @@ class StockReconciliation(StockController):
if item.has_serial_no or item.has_batch_no:
has_serial_no = True
self.get_sle_for_serialized_items(row, sl_entries)
self.get_sle_for_serialized_items(row, sl_entries, item)
else:
if row.serial_no or row.batch_no:
frappe.throw(
@@ -281,7 +281,7 @@ class StockReconciliation(StockController):
if has_serial_no and sl_entries:
self.update_valuation_rate_for_serial_no()
def get_sle_for_serialized_items(self, row, sl_entries):
def get_sle_for_serialized_items(self, row, sl_entries, item):
from erpnext.stock.stock_ledger import get_previous_sle
serial_nos = get_serial_nos(row.serial_no)
@@ -347,6 +347,9 @@ class StockReconciliation(StockController):
if row.qty:
args = self.get_sle_for_items(row)
if item.has_serial_no and item.has_batch_no:
args["qty_after_transaction"] = row.qty
args.update(
{
"actual_qty": row.qty,

View File

@@ -643,6 +643,38 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
)
self.assertEqual(len(active_sr_no), 0)
def test_serial_no_batch_no_item(self):
item = self.make_item(
"Test Serial No Batch No Item",
{
"is_stock_item": 1,
"has_serial_no": 1,
"has_batch_no": 1,
"serial_no_series": "SRS9.####",
"batch_number_series": "BNS9.####",
"create_new_batch": 1,
},
)
warehouse = "_Test Warehouse - _TC"
sr = create_stock_reconciliation(
item_code=item.name,
warehouse=warehouse,
qty=1,
rate=100,
)
sl_entry = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Stock Reconciliation", "voucher_no": sr.name},
["actual_qty", "qty_after_transaction"],
as_dict=1,
)
self.assertEqual(flt(sl_entry.actual_qty), 1.0)
self.assertEqual(flt(sl_entry.qty_after_transaction), 1.0)
def create_batch_item_with_batch(item_name, batch_id):
batch_item_doc = create_item(item_name, is_stock_item=1)