mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-27 17:04:47 +00:00
Merge pull request #34160 from frappe/version-13-hotfix
chore: release v13
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
# the repo. Unless a later match takes precedence,
|
# the repo. Unless a later match takes precedence,
|
||||||
|
|
||||||
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||||
erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
erpnext/assets/ @anandbaburajan @deepeshgarg007
|
||||||
erpnext/erpnext_integrations/ @nextchamp-saqib
|
erpnext/erpnext_integrations/ @nextchamp-saqib
|
||||||
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
|
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
|
||||||
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||||
|
|||||||
@@ -248,21 +248,16 @@ class JournalEntry(AccountsController):
|
|||||||
):
|
):
|
||||||
processed_assets.append(d.reference_name)
|
processed_assets.append(d.reference_name)
|
||||||
|
|
||||||
asset = frappe.db.get_value(
|
asset = frappe.get_doc("Asset", d.reference_name)
|
||||||
"Asset", d.reference_name, ["calculate_depreciation", "value_after_depreciation"], as_dict=1
|
|
||||||
)
|
|
||||||
|
|
||||||
if asset.calculate_depreciation:
|
if asset.calculate_depreciation:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
depr_value = d.debit or d.credit
|
depr_value = d.debit or d.credit
|
||||||
|
|
||||||
frappe.db.set_value(
|
asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value)
|
||||||
"Asset",
|
|
||||||
d.reference_name,
|
asset.set_status()
|
||||||
"value_after_depreciation",
|
|
||||||
asset.value_after_depreciation - depr_value,
|
|
||||||
)
|
|
||||||
|
|
||||||
def update_inter_company_jv(self):
|
def update_inter_company_jv(self):
|
||||||
if (
|
if (
|
||||||
@@ -351,12 +346,9 @@ class JournalEntry(AccountsController):
|
|||||||
else:
|
else:
|
||||||
depr_value = d.debit or d.credit
|
depr_value = d.debit or d.credit
|
||||||
|
|
||||||
frappe.db.set_value(
|
asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value)
|
||||||
"Asset",
|
|
||||||
d.reference_name,
|
asset.set_status()
|
||||||
"value_after_depreciation",
|
|
||||||
asset.value_after_depreciation + depr_value,
|
|
||||||
)
|
|
||||||
|
|
||||||
def unlink_inter_company_jv(self):
|
def unlink_inter_company_jv(self):
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -21,8 +21,24 @@ class POSClosingEntry(StatusUpdater):
|
|||||||
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
|
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"))
|
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
|
||||||
|
|
||||||
|
self.validate_duplicate_pos_invoices()
|
||||||
self.validate_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):
|
def validate_pos_invoices(self):
|
||||||
invalid_rows = []
|
invalid_rows = []
|
||||||
for d in self.pos_transactions:
|
for d in self.pos_transactions:
|
||||||
|
|||||||
@@ -18,6 +18,22 @@ class POSInvoiceMergeLog(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_customer()
|
self.validate_customer()
|
||||||
self.validate_pos_invoice_status()
|
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):
|
def validate_customer(self):
|
||||||
if self.merge_invoices_based_on == "Customer Group":
|
if self.merge_invoices_based_on == "Customer Group":
|
||||||
@@ -427,6 +443,8 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
|
|||||||
|
|
||||||
if closing_entry:
|
if closing_entry:
|
||||||
closing_entry.set_status(update=True, status="Failed")
|
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)
|
closing_entry.db_set("error_message", error_message)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|||||||
@@ -256,7 +256,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)
|
tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers)
|
||||||
|
|
||||||
if cint(tax_details.round_off_tax_amount):
|
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
|
return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount
|
||||||
|
|
||||||
@@ -555,3 +555,20 @@ def is_valid_certificate(
|
|||||||
valid = True
|
valid = True
|
||||||
|
|
||||||
return valid
|
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
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ frappe.ui.form.on('Asset', {
|
|||||||
$.each(depr_entries || [], function(i, v) {
|
$.each(depr_entries || [], function(i, v) {
|
||||||
x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' }));
|
x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' }));
|
||||||
let last_asset_value = asset_values[asset_values.length - 1]
|
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')));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -667,11 +667,15 @@ class Asset(AccountsController):
|
|||||||
|
|
||||||
if self.journal_entry_for_scrap:
|
if self.journal_entry_for_scrap:
|
||||||
status = "Scrapped"
|
status = "Scrapped"
|
||||||
elif self.finance_books:
|
else:
|
||||||
idx = self.get_default_finance_book_idx() or 0
|
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
|
if self.calculate_depreciation:
|
||||||
value_after_depreciation = self.finance_books[idx].value_after_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:
|
if flt(value_after_depreciation) <= expected_value_after_useful_life:
|
||||||
status = "Fully Depreciated"
|
status = "Fully Depreciated"
|
||||||
@@ -703,24 +707,6 @@ class Asset(AccountsController):
|
|||||||
if d.finance_book == self.default_finance_book:
|
if d.finance_book == self.default_finance_book:
|
||||||
return cint(d.idx) - 1
|
return cint(d.idx) - 1
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_manual_depreciation_entries(self):
|
|
||||||
(_, _, depreciation_expense_account) = get_depreciation_accounts(self)
|
|
||||||
|
|
||||||
gle = frappe.qb.DocType("GL Entry")
|
|
||||||
|
|
||||||
records = (
|
|
||||||
frappe.qb.from_(gle)
|
|
||||||
.select(gle.voucher_no.as_("name"), gle.debit.as_("value"), gle.posting_date)
|
|
||||||
.where(gle.against_voucher == self.name)
|
|
||||||
.where(gle.account == depreciation_expense_account)
|
|
||||||
.where(gle.debit != 0)
|
|
||||||
.where(gle.is_cancelled == 0)
|
|
||||||
.orderby(gle.posting_date)
|
|
||||||
).run(as_dict=True)
|
|
||||||
|
|
||||||
return records
|
|
||||||
|
|
||||||
def validate_make_gl_entry(self):
|
def validate_make_gl_entry(self):
|
||||||
purchase_document = self.get_purchase_document()
|
purchase_document = self.get_purchase_document()
|
||||||
if not purchase_document:
|
if not purchase_document:
|
||||||
@@ -837,6 +823,25 @@ class Asset(AccountsController):
|
|||||||
make_gl_entries(gl_entries)
|
make_gl_entries(gl_entries)
|
||||||
self.db_set("booked_fixed_asset", 1)
|
self.db_set("booked_fixed_asset", 1)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_manual_depreciation_entries(self):
|
||||||
|
(_, _, depreciation_expense_account) = get_depreciation_accounts(self)
|
||||||
|
|
||||||
|
gle = frappe.qb.DocType("GL Entry")
|
||||||
|
|
||||||
|
records = (
|
||||||
|
frappe.qb.from_(gle)
|
||||||
|
.select(gle.voucher_no.as_("name"), gle.debit.as_("value"), gle.posting_date)
|
||||||
|
.where(gle.against_voucher == self.name)
|
||||||
|
.where(gle.account == depreciation_expense_account)
|
||||||
|
.where(gle.debit != 0)
|
||||||
|
.where(gle.is_cancelled == 0)
|
||||||
|
.orderby(gle.posting_date)
|
||||||
|
.orderby(gle.creation)
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_depreciation_rate(self, args, on_validate=False):
|
def get_depreciation_rate(self, args, on_validate=False):
|
||||||
if isinstance(args, string_types):
|
if isinstance(args, string_types):
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ def make_depreciation_entry(asset_name, date=None):
|
|||||||
finance_books.value_after_depreciation -= d.depreciation_amount
|
finance_books.value_after_depreciation -= d.depreciation_amount
|
||||||
finance_books.db_update()
|
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()
|
asset.set_status()
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ class AssetRepair(AccountsController):
|
|||||||
self.asset_doc.prepare_depreciation_data()
|
self.asset_doc.prepare_depreciation_data()
|
||||||
self.asset_doc.save()
|
self.asset_doc.save()
|
||||||
|
|
||||||
|
def after_delete(self):
|
||||||
|
frappe.get_doc("Asset", self.asset).set_status()
|
||||||
|
|
||||||
def check_repair_status(self):
|
def check_repair_status(self):
|
||||||
if self.repair_status == "Pending":
|
if self.repair_status == "Pending":
|
||||||
frappe.throw(_("Please update Repair Status."))
|
frappe.throw(_("Please update Repair Status."))
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ def prepare_chart_data(data, filters):
|
|||||||
filters.filter_based_on,
|
filters.filter_based_on,
|
||||||
"Monthly",
|
"Monthly",
|
||||||
company=filters.company,
|
company=filters.company,
|
||||||
|
ignore_fiscal_year=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
for d in period_list:
|
for d in period_list:
|
||||||
|
|||||||
@@ -2,53 +2,60 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.query_reports["Employee Leave Balance"] = {
|
frappe.query_reports["Employee Leave Balance"] = {
|
||||||
"filters": [
|
filters: [
|
||||||
{
|
{
|
||||||
"fieldname": "from_date",
|
fieldname: "from_date",
|
||||||
"label": __("From Date"),
|
label: __("From Date"),
|
||||||
"fieldtype": "Date",
|
fieldtype: "Date",
|
||||||
"reqd": 1,
|
reqd: 1,
|
||||||
"default": frappe.defaults.get_default("year_start_date")
|
default: frappe.defaults.get_default("year_start_date")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "to_date",
|
fieldname: "to_date",
|
||||||
"label": __("To Date"),
|
label: __("To Date"),
|
||||||
"fieldtype": "Date",
|
fieldtype: "Date",
|
||||||
"reqd": 1,
|
reqd: 1,
|
||||||
"default": frappe.defaults.get_default("year_end_date")
|
default: frappe.defaults.get_default("year_end_date")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "company",
|
label: __("Company"),
|
||||||
"label": __("Company"),
|
fieldname: "company",
|
||||||
"fieldtype": "Link",
|
fieldtype: "Link",
|
||||||
"options": "Company",
|
options: "Company",
|
||||||
"reqd": 1,
|
reqd: 1,
|
||||||
"default": frappe.defaults.get_user_default("Company")
|
default: frappe.defaults.get_user_default("Company")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "department",
|
fieldname: "department",
|
||||||
"label": __("Department"),
|
label: __("Department"),
|
||||||
"fieldtype": "Link",
|
fieldtype: "Link",
|
||||||
"options": "Department",
|
options: "Department",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "employee",
|
fieldname: "employee",
|
||||||
"label": __("Employee"),
|
label: __("Employee"),
|
||||||
"fieldtype": "Link",
|
fieldtype: "Link",
|
||||||
"options": "Employee",
|
options: "Employee",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "employee_status",
|
fieldname: "employee_status",
|
||||||
"label": __("Employee Status"),
|
label: __("Employee Status"),
|
||||||
"fieldtype": "Select",
|
fieldtype: "Select",
|
||||||
"options": [
|
options: [
|
||||||
"",
|
"",
|
||||||
{ "value": "Active", "label": __("Active") },
|
{ "value": "Active", "label": __("Active") },
|
||||||
{ "value": "Inactive", "label": __("Inactive") },
|
{ "value": "Inactive", "label": __("Inactive") },
|
||||||
{ "value": "Suspended", "label": __("Suspended") },
|
{ "value": "Suspended", "label": __("Suspended") },
|
||||||
{ "value": "Left", "label": __("Left") },
|
{ "value": "Left", "label": __("Left") },
|
||||||
],
|
],
|
||||||
"default": "Active",
|
default: "Active",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "consolidate_leave_types",
|
||||||
|
label: __("Consolidate Leave Types"),
|
||||||
|
fieldtype: "Check",
|
||||||
|
default: 1,
|
||||||
|
depends_on: "eval: !doc.employee",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from typing import Dict, List, Optional, Tuple
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import add_days, getdate
|
from frappe.utils import add_days, cint, getdate
|
||||||
|
|
||||||
from erpnext.hr.doctype.leave_allocation.leave_allocation import get_previous_allocation
|
from erpnext.hr.doctype.leave_allocation.leave_allocation import get_previous_allocation
|
||||||
from erpnext.hr.doctype.leave_application.leave_application import (
|
from erpnext.hr.doctype.leave_application.leave_application import (
|
||||||
@@ -24,7 +24,7 @@ def execute(filters: Optional[Filters] = None) -> Tuple:
|
|||||||
|
|
||||||
columns = get_columns()
|
columns = get_columns()
|
||||||
data = get_data(filters)
|
data = get_data(filters)
|
||||||
charts = get_chart_data(data)
|
charts = get_chart_data(data, filters)
|
||||||
return columns, data, None, charts
|
return columns, data, None, charts
|
||||||
|
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ def get_data(filters: Filters) -> List:
|
|||||||
conditions = get_conditions(filters)
|
conditions = get_conditions(filters)
|
||||||
|
|
||||||
user = frappe.session.user
|
user = frappe.session.user
|
||||||
department_approver_map = get_department_leave_approver_map(filters.get("department"))
|
department_approver_map = get_department_leave_approver_map(filters.department)
|
||||||
|
|
||||||
active_employees = frappe.get_list(
|
active_employees = frappe.get_list(
|
||||||
"Employee",
|
"Employee",
|
||||||
@@ -97,22 +97,27 @@ def get_data(filters: Filters) -> List:
|
|||||||
fields=["name", "employee_name", "department", "user_id", "leave_approver"],
|
fields=["name", "employee_name", "department", "user_id", "leave_approver"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
precision = cint(frappe.db.get_single_value("System Settings", "float_precision", cache=True))
|
||||||
|
consolidate_leave_types = len(active_employees) > 1 and filters.consolidate_leave_types
|
||||||
|
row = None
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
for leave_type in leave_types:
|
for leave_type in leave_types:
|
||||||
if len(active_employees) > 1:
|
if consolidate_leave_types:
|
||||||
data.append({"leave_type": leave_type})
|
data.append({"leave_type": leave_type})
|
||||||
else:
|
else:
|
||||||
row = frappe._dict({"leave_type": leave_type})
|
row = frappe._dict({"leave_type": leave_type})
|
||||||
|
|
||||||
for employee in active_employees:
|
for employee in active_employees:
|
||||||
|
|
||||||
leave_approvers = department_approver_map.get(employee.department_name, []).append(
|
leave_approvers = department_approver_map.get(employee.department_name, []).append(
|
||||||
employee.leave_approver
|
employee.leave_approver
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(active_employees) > 1:
|
if consolidate_leave_types:
|
||||||
row = frappe._dict()
|
row = frappe._dict()
|
||||||
|
else:
|
||||||
|
row = frappe._dict({"leave_type": leave_type})
|
||||||
|
|
||||||
row.employee = employee.name
|
row.employee = employee.name
|
||||||
row.employee_name = employee.employee_name
|
row.employee_name = employee.employee_name
|
||||||
@@ -166,17 +171,17 @@ def get_opening_balance(
|
|||||||
def get_conditions(filters: Filters) -> Dict:
|
def get_conditions(filters: Filters) -> Dict:
|
||||||
conditions = {}
|
conditions = {}
|
||||||
|
|
||||||
if filters.get("employee"):
|
if filters.employee:
|
||||||
conditions["name"] = filters.get("employee")
|
conditions["name"] = filters.employee
|
||||||
|
|
||||||
if filters.get("company"):
|
if filters.company:
|
||||||
conditions["company"] = filters.get("company")
|
conditions["company"] = filters.company
|
||||||
|
|
||||||
if filters.get("department"):
|
if filters.department:
|
||||||
conditions["department"] = filters.get("department")
|
conditions["department"] = filters.department
|
||||||
|
|
||||||
if filters.get("employee_status"):
|
if filters.employee_status:
|
||||||
conditions["status"] = filters.get("employee_status")
|
conditions["status"] = filters.employee_status
|
||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
@@ -268,12 +273,15 @@ def get_leave_ledger_entries(
|
|||||||
return records
|
return records
|
||||||
|
|
||||||
|
|
||||||
def get_chart_data(data: List) -> Dict:
|
def get_chart_data(data: List, filters: Filters) -> Dict:
|
||||||
labels = []
|
labels = []
|
||||||
datasets = []
|
datasets = []
|
||||||
employee_data = data
|
employee_data = data
|
||||||
|
|
||||||
if data and data[0].get("employee_name"):
|
if not data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if data and filters.employee:
|
||||||
get_dataset_for_chart(employee_data, datasets, labels)
|
get_dataset_for_chart(employee_data, datasets, labels)
|
||||||
|
|
||||||
chart = {
|
chart = {
|
||||||
|
|||||||
@@ -119,7 +119,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
|||||||
frappe.model.round_floats_in(item);
|
frappe.model.round_floats_in(item);
|
||||||
item.net_rate = item.rate;
|
item.net_rate = item.rate;
|
||||||
item.qty = item.qty === undefined ? (me.frm.doc.is_return ? -1 : 1) : item.qty;
|
item.qty = item.qty === undefined ? (me.frm.doc.is_return ? -1 : 1) : item.qty;
|
||||||
item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item));
|
|
||||||
|
if (!(me.frm.doc.is_return || me.frm.doc.is_debit_note)) {
|
||||||
|
item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let qty = item.qty || 1;
|
||||||
|
qty = me.frm.doc.is_return ? -1 * qty : qty;
|
||||||
|
item.net_amount = item.amount = flt(item.rate * qty, precision("amount", item));
|
||||||
|
}
|
||||||
|
|
||||||
item.item_tax_amount = 0.0;
|
item.item_tax_amount = 0.0;
|
||||||
item.total_weight = flt(item.weight_per_unit * item.stock_qty);
|
item.total_weight = flt(item.weight_per_unit * item.stock_qty);
|
||||||
|
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ def get_sales_order_details(company_list, filters):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if filters.get("item_group"):
|
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"):
|
if filters.get("from_date"):
|
||||||
query = query.where(db_so.transaction_date >= filters.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)
|
query = query.where(db_so.transaction_date <= filters.to_date)
|
||||||
|
|
||||||
if filters.get("item_code"):
|
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"):
|
if filters.get("customer"):
|
||||||
query = query.where(db_so.customer == filters.customer)
|
query = query.where(db_so.customer == filters.customer)
|
||||||
|
|||||||
@@ -97,12 +97,12 @@ frappe.ui.form.on("Delivery Note", {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
|
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) {
|
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";
|
"Inter Company Purchase Receipt";
|
||||||
|
|
||||||
me.frm.add_custom_button(button_label, function() {
|
frm.add_custom_button(__(button_label), function() {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
|
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
|
||||||
frm: frm,
|
frm: frm,
|
||||||
|
|||||||
@@ -38,5 +38,19 @@
|
|||||||
"price_list_rate": 1000,
|
"price_list_rate": 1000,
|
||||||
"valid_from": "2017-04-10",
|
"valid_from": "2017-04-10",
|
||||||
"valid_upto": "2017-04-17"
|
"valid_upto": "2017-04-17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Item Price",
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"price_list": "_Test Buying Price List",
|
||||||
|
"price_list_rate": 100,
|
||||||
|
"supplier": "_Test Supplier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Item Price",
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"price_list": "_Test Selling Price List",
|
||||||
|
"price_list_rate": 200,
|
||||||
|
"customer": "_Test Customer"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -31,5 +31,21 @@
|
|||||||
"enabled": 1,
|
"enabled": 1,
|
||||||
"price_list_name": "_Test Price List Rest of the World",
|
"price_list_name": "_Test Price List Rest of the World",
|
||||||
"selling": 1
|
"selling": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buying": 0,
|
||||||
|
"currency": "USD",
|
||||||
|
"doctype": "Price List",
|
||||||
|
"enabled": 1,
|
||||||
|
"price_list_name": "_Test Selling Price List",
|
||||||
|
"selling": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buying": 1,
|
||||||
|
"currency": "USD",
|
||||||
|
"doctype": "Price List",
|
||||||
|
"enabled": 1,
|
||||||
|
"price_list_name": "_Test Buying Price List",
|
||||||
|
"selling": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -88,8 +88,15 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
|
|||||||
|
|
||||||
update_party_blanket_order(args, out)
|
update_party_blanket_order(args, out)
|
||||||
|
|
||||||
|
# Never try to find a customer price if customer is set in these Doctype
|
||||||
|
current_customer = args.customer
|
||||||
|
if args.get("doctype") in ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]:
|
||||||
|
args.customer = None
|
||||||
|
|
||||||
out.update(get_price_list_rate(args, item))
|
out.update(get_price_list_rate(args, item))
|
||||||
|
|
||||||
|
args.customer = current_customer
|
||||||
|
|
||||||
if args.customer and cint(args.is_pos):
|
if args.customer and cint(args.is_pos):
|
||||||
out.update(get_pos_profile_item_details(args.company, args, update_data=True))
|
out.update(get_pos_profile_item_details(args.company, args, update_data=True))
|
||||||
|
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ def get_reserved_qty(item_code, warehouse):
|
|||||||
and parenttype="Sales Order"
|
and parenttype="Sales Order"
|
||||||
and item_code != parent_item
|
and item_code != parent_item
|
||||||
and exists (select * from `tabSales Order` so
|
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)
|
) dnpi)
|
||||||
union
|
union
|
||||||
(select stock_qty as dnpi_qty, qty as so_item_qty,
|
(select stock_qty as dnpi_qty, qty as so_item_qty,
|
||||||
@@ -132,7 +132,7 @@ def get_reserved_qty(item_code, warehouse):
|
|||||||
and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0)
|
and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0)
|
||||||
and exists(select * from `tabSales Order` so
|
and exists(select * from `tabSales Order` so
|
||||||
where so.name = so_item.parent and so.docstatus = 1
|
where so.name = so_item.parent and so.docstatus = 1
|
||||||
and so.status != 'Closed'))
|
and so.status not in ('On Hold', 'Closed')))
|
||||||
) tab
|
) tab
|
||||||
where
|
where
|
||||||
so_item_qty >= so_item_delivered_qty
|
so_item_qty >= so_item_delivered_qty
|
||||||
|
|||||||
40
erpnext/stock/tests/test_get_item_details.py
Normal file
40
erpnext/stock/tests/test_get_item_details.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.test_runner import make_test_records
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
|
|
||||||
|
test_ignore = ["BOM"]
|
||||||
|
test_dependencies = ["Customer", "Supplier", "Item", "Price List", "Item Price"]
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetItemDetail(FrappeTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
make_test_records("Price List")
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
def test_get_item_detail_purchase_order(self):
|
||||||
|
|
||||||
|
args = frappe._dict(
|
||||||
|
{
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
"company": "_Test Company",
|
||||||
|
"customer": "_Test Customer",
|
||||||
|
"conversion_rate": 1.0,
|
||||||
|
"price_list_currency": "USD",
|
||||||
|
"plc_conversion_rate": 1.0,
|
||||||
|
"doctype": "Purchase Order",
|
||||||
|
"name": None,
|
||||||
|
"supplier": "_Test Supplier",
|
||||||
|
"transaction_date": None,
|
||||||
|
"conversion_rate": 1.0,
|
||||||
|
"price_list": "_Test Buying Price List",
|
||||||
|
"is_subcontracted": 0,
|
||||||
|
"ignore_pricing_rule": 1,
|
||||||
|
"qty": 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
details = get_item_details(args)
|
||||||
|
self.assertEqual(details.get("price_list_rate"), 100)
|
||||||
@@ -51,36 +51,31 @@ def get_tabs(categories):
|
|||||||
return tab_values
|
return tab_values
|
||||||
|
|
||||||
|
|
||||||
def get_category_records(categories):
|
def get_category_records(categories: list):
|
||||||
categorical_data = {}
|
categorical_data = {}
|
||||||
for category in categories:
|
|
||||||
if category == "item_group":
|
for c in categories:
|
||||||
categorical_data["item_group"] = frappe.db.sql(
|
if c == "item_group":
|
||||||
"""
|
categorical_data["item_group"] = frappe.db.get_all(
|
||||||
Select
|
"Item Group",
|
||||||
name, parent_item_group, is_group, image, route
|
filters={"parent_item_group": "All Item Groups", "show_in_website": 1},
|
||||||
from
|
fields=["name", "parent_item_group", "is_group", "image", "route"],
|
||||||
`tabItem Group`
|
|
||||||
where
|
|
||||||
parent_item_group = 'All Item Groups'
|
|
||||||
and show_in_website = 1
|
|
||||||
""",
|
|
||||||
as_dict=1,
|
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
doctype = frappe.unscrub(category)
|
continue
|
||||||
fields = ["name"]
|
|
||||||
if frappe.get_meta(doctype, cached=True).get_field("image"):
|
doctype = frappe.unscrub(c)
|
||||||
|
fields = ["name"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
meta = frappe.get_meta(doctype, cached=True)
|
||||||
|
if meta.get_field("image"):
|
||||||
fields += ["image"]
|
fields += ["image"]
|
||||||
|
|
||||||
categorical_data[category] = frappe.db.sql(
|
data = frappe.db.get_all(doctype, fields=fields)
|
||||||
f"""
|
categorical_data[c] = data
|
||||||
Select
|
except BaseException:
|
||||||
{",".join(fields)}
|
frappe.throw(_("DocType {} not found").format(doctype))
|
||||||
from
|
continue
|
||||||
`tab{doctype}`
|
|
||||||
""",
|
|
||||||
as_dict=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
return categorical_data
|
return categorical_data
|
||||||
|
|||||||
Reference in New Issue
Block a user