mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-03 20:29:09 +00:00
Merge pull request #44341 from frappe/version-15-hotfix
chore: release v15
This commit is contained in:
@@ -515,6 +515,55 @@ class TestJournalEntry(unittest.TestCase):
|
|||||||
self.assertEqual(row.debit_in_account_currency, 100)
|
self.assertEqual(row.debit_in_account_currency, 100)
|
||||||
self.assertEqual(row.credit_in_account_currency, 100)
|
self.assertEqual(row.credit_in_account_currency, 100)
|
||||||
|
|
||||||
|
def test_toggle_debit_credit_if_negative(self):
|
||||||
|
from erpnext.accounts.general_ledger import process_gl_map
|
||||||
|
|
||||||
|
# Create JV with defaut cost center - _Test Cost Center
|
||||||
|
frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
|
||||||
|
|
||||||
|
jv = frappe.new_doc("Journal Entry")
|
||||||
|
jv.posting_date = nowdate()
|
||||||
|
jv.company = "_Test Company"
|
||||||
|
jv.user_remark = "test"
|
||||||
|
jv.extend(
|
||||||
|
"accounts",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"account": "_Test Cash - _TC",
|
||||||
|
"debit": 100 * -1,
|
||||||
|
"debit_in_account_currency": 100 * -1,
|
||||||
|
"exchange_rate": 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"account": "_Test Bank - _TC",
|
||||||
|
"credit": 100 * -1,
|
||||||
|
"credit_in_account_currency": 100 * -1,
|
||||||
|
"exchange_rate": 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
jv.flags.ignore_validate = True
|
||||||
|
jv.save()
|
||||||
|
|
||||||
|
self.assertEqual(len(jv.accounts), 2)
|
||||||
|
|
||||||
|
gl_map = jv.build_gl_map()
|
||||||
|
|
||||||
|
for row in gl_map:
|
||||||
|
if row.account == "_Test Cash - _TC":
|
||||||
|
self.assertEqual(row.debit, 100 * -1)
|
||||||
|
self.assertEqual(row.debit_in_account_currency, 100 * -1)
|
||||||
|
self.assertEqual(row.debit_in_transaction_currency, 100 * -1)
|
||||||
|
|
||||||
|
gl_map = process_gl_map(gl_map, False)
|
||||||
|
|
||||||
|
for row in gl_map:
|
||||||
|
if row.account == "_Test Cash - _TC":
|
||||||
|
self.assertEqual(row.credit, 100)
|
||||||
|
self.assertEqual(row.credit_in_account_currency, 100)
|
||||||
|
self.assertEqual(row.credit_in_transaction_currency, 100)
|
||||||
|
|
||||||
def test_transaction_exchange_rate_on_journals(self):
|
def test_transaction_exchange_rate_on_journals(self):
|
||||||
jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable USD - _TC", 100, save=False)
|
jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable USD - _TC", 100, save=False)
|
||||||
jv.accounts[0].update({"debit_in_account_currency": 8500, "exchange_rate": 1})
|
jv.accounts[0].update({"debit_in_account_currency": 8500, "exchange_rate": 1})
|
||||||
|
|||||||
@@ -1219,11 +1219,19 @@ class PaymentEntry(AccountsController):
|
|||||||
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
|
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
|
||||||
|
|
||||||
gle.update(
|
gle.update(
|
||||||
{
|
self.get_gl_dict(
|
||||||
dr_or_cr: allocated_amount_in_company_currency,
|
{
|
||||||
dr_or_cr + "_in_account_currency": d.allocated_amount,
|
"account": self.party_account,
|
||||||
"cost_center": cost_center,
|
"party_type": self.party_type,
|
||||||
}
|
"party": self.party,
|
||||||
|
"against": against_account,
|
||||||
|
"account_currency": self.party_account_currency,
|
||||||
|
"cost_center": cost_center,
|
||||||
|
dr_or_cr + "_in_account_currency": d.allocated_amount,
|
||||||
|
dr_or_cr: allocated_amount_in_company_currency,
|
||||||
|
},
|
||||||
|
item=self,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.book_advance_payments_in_separate_party_account:
|
if self.book_advance_payments_in_separate_party_account:
|
||||||
@@ -1746,7 +1754,7 @@ class PaymentEntry(AccountsController):
|
|||||||
if paid_amount > total_negative_outstanding:
|
if paid_amount > total_negative_outstanding:
|
||||||
if total_negative_outstanding == 0:
|
if total_negative_outstanding == 0:
|
||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
_("Cannot {0} from {2} without any negative outstanding invoice").format(
|
_("Cannot {0} from {1} without any negative outstanding invoice").format(
|
||||||
self.payment_type,
|
self.payment_type,
|
||||||
self.party_type,
|
self.party_type,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import json
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _, qb
|
from frappe import _, qb
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.query_builder.functions import Sum
|
from frappe.query_builder.functions import Abs, Sum
|
||||||
from frappe.utils import flt, nowdate
|
from frappe.utils import flt, nowdate
|
||||||
from frappe.utils.background_jobs import enqueue
|
from frappe.utils.background_jobs import enqueue
|
||||||
|
|
||||||
@@ -564,6 +564,8 @@ def make_payment_request(**args):
|
|||||||
# fetches existing payment request `grand_total` amount
|
# fetches existing payment request `grand_total` amount
|
||||||
existing_payment_request_amount = get_existing_payment_request_amount(ref_doc.doctype, ref_doc.name)
|
existing_payment_request_amount = get_existing_payment_request_amount(ref_doc.doctype, ref_doc.name)
|
||||||
|
|
||||||
|
existing_paid_amount = get_existing_paid_amount(ref_doc.doctype, ref_doc.name)
|
||||||
|
|
||||||
def validate_and_calculate_grand_total(grand_total, existing_payment_request_amount):
|
def validate_and_calculate_grand_total(grand_total, existing_payment_request_amount):
|
||||||
grand_total -= existing_payment_request_amount
|
grand_total -= existing_payment_request_amount
|
||||||
if not grand_total:
|
if not grand_total:
|
||||||
@@ -583,6 +585,15 @@ def make_payment_request(**args):
|
|||||||
else:
|
else:
|
||||||
grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount)
|
grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount)
|
||||||
|
|
||||||
|
if existing_paid_amount:
|
||||||
|
if ref_doc.party_account_currency == ref_doc.currency:
|
||||||
|
if ref_doc.conversion_rate:
|
||||||
|
grand_total -= flt(existing_paid_amount / ref_doc.conversion_rate)
|
||||||
|
else:
|
||||||
|
grand_total -= flt(existing_paid_amount)
|
||||||
|
else:
|
||||||
|
grand_total -= flt(existing_paid_amount / ref_doc.conversion_rate)
|
||||||
|
|
||||||
if draft_payment_request:
|
if draft_payment_request:
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
"Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False
|
"Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False
|
||||||
@@ -753,6 +764,27 @@ def get_existing_payment_request_amount(ref_dt, ref_dn, statuses: list | None =
|
|||||||
return response[0][0] if response[0] else 0
|
return response[0][0] if response[0] else 0
|
||||||
|
|
||||||
|
|
||||||
|
def get_existing_paid_amount(doctype, name):
|
||||||
|
PL = frappe.qb.DocType("Payment Ledger Entry")
|
||||||
|
PER = frappe.qb.DocType("Payment Entry Reference")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(PL)
|
||||||
|
.left_join(PER)
|
||||||
|
.on(
|
||||||
|
(PER.reference_doctype == PL.against_voucher_type) & (PER.reference_name == PL.against_voucher_no)
|
||||||
|
)
|
||||||
|
.select(Abs(Sum(PL.amount)).as_("total_paid_amount"))
|
||||||
|
.where(PL.against_voucher_type.eq(doctype))
|
||||||
|
.where(PL.against_voucher_no.eq(name))
|
||||||
|
.where(PL.amount < 0)
|
||||||
|
.where(PER.payment_request.isnull())
|
||||||
|
)
|
||||||
|
response = query.run()
|
||||||
|
|
||||||
|
return response[0][0] if response[0] else 0
|
||||||
|
|
||||||
|
|
||||||
def get_gateway_details(args): # nosemgrep
|
def get_gateway_details(args): # nosemgrep
|
||||||
"""
|
"""
|
||||||
Return gateway and payment account of default payment gateway
|
Return gateway and payment account of default payment gateway
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import unittest
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
|
||||||
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
|
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
|
||||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
@@ -524,3 +525,21 @@ class TestPaymentRequest(FrappeTestCase):
|
|||||||
self.assertEqual(pr.grand_total, 1000)
|
self.assertEqual(pr.grand_total, 1000)
|
||||||
|
|
||||||
so.load_from_db()
|
so.load_from_db()
|
||||||
|
|
||||||
|
def test_partial_paid_invoice_with_payment_request(self):
|
||||||
|
si = create_sales_invoice(currency="INR", qty=1, rate=5000)
|
||||||
|
si.save()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||||
|
pe.reference_no = "PAYEE0002"
|
||||||
|
pe.reference_date = frappe.utils.nowdate()
|
||||||
|
pe.paid_amount = 2500
|
||||||
|
pe.references[0].allocated_amount = 2500
|
||||||
|
pe.save()
|
||||||
|
pe.submit()
|
||||||
|
|
||||||
|
si.load_from_db()
|
||||||
|
pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1)
|
||||||
|
|
||||||
|
self.assertEqual(pr.grand_total, si.outstanding_amount)
|
||||||
|
|||||||
@@ -171,9 +171,7 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
pl_account_balances = self.get_account_balances_based_on_dimensions(report_type="Profit and Loss")
|
pl_account_balances = self.get_account_balances_based_on_dimensions(report_type="Profit and Loss")
|
||||||
for dimensions, account_balances in pl_account_balances.items():
|
for dimensions, account_balances in pl_account_balances.items():
|
||||||
for acc, balances in account_balances.items():
|
for acc, balances in account_balances.items():
|
||||||
balance_in_company_currency = flt(balances.debit_in_account_currency) - flt(
|
balance_in_company_currency = flt(balances.debit) - flt(balances.credit)
|
||||||
balances.credit_in_account_currency
|
|
||||||
)
|
|
||||||
if balance_in_company_currency and acc != "balances":
|
if balance_in_company_currency and acc != "balances":
|
||||||
self.pl_accounts_reverse_gle.append(
|
self.pl_accounts_reverse_gle.append(
|
||||||
self.get_gle_for_pl_account(acc, balances, dimensions)
|
self.get_gle_for_pl_account(acc, balances, dimensions)
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ frappe.ui.form.on("POS Closing Entry", {
|
|||||||
frm.doc.grand_total += flt(doc.grand_total);
|
frm.doc.grand_total += flt(doc.grand_total);
|
||||||
frm.doc.net_total += flt(doc.net_total);
|
frm.doc.net_total += flt(doc.net_total);
|
||||||
frm.doc.total_quantity += flt(doc.total_qty);
|
frm.doc.total_quantity += flt(doc.total_qty);
|
||||||
refresh_payments(doc, frm);
|
refresh_payments(doc, frm, false);
|
||||||
refresh_taxes(doc, frm);
|
refresh_taxes(doc, frm);
|
||||||
refresh_fields(frm);
|
refresh_fields(frm);
|
||||||
set_html_data(frm);
|
set_html_data(frm);
|
||||||
@@ -172,7 +172,7 @@ function set_form_data(data, frm) {
|
|||||||
frm.doc.grand_total += flt(d.grand_total);
|
frm.doc.grand_total += flt(d.grand_total);
|
||||||
frm.doc.net_total += flt(d.net_total);
|
frm.doc.net_total += flt(d.net_total);
|
||||||
frm.doc.total_quantity += flt(d.total_qty);
|
frm.doc.total_quantity += flt(d.total_qty);
|
||||||
refresh_payments(d, frm);
|
refresh_payments(d, frm, true);
|
||||||
refresh_taxes(d, frm);
|
refresh_taxes(d, frm);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,7 @@ function add_to_pos_transaction(d, frm) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh_payments(d, frm) {
|
function refresh_payments(d, frm, is_new) {
|
||||||
d.payments.forEach((p) => {
|
d.payments.forEach((p) => {
|
||||||
const payment = frm.doc.payment_reconciliation.find(
|
const payment = frm.doc.payment_reconciliation.find(
|
||||||
(pay) => pay.mode_of_payment === p.mode_of_payment
|
(pay) => pay.mode_of_payment === p.mode_of_payment
|
||||||
@@ -196,9 +196,7 @@ function refresh_payments(d, frm) {
|
|||||||
}
|
}
|
||||||
if (payment) {
|
if (payment) {
|
||||||
payment.expected_amount += flt(p.amount);
|
payment.expected_amount += flt(p.amount);
|
||||||
if (payment.closing_amount === 0) {
|
if (is_new) payment.closing_amount = payment.expected_amount;
|
||||||
payment.closing_amount = payment.expected_amount;
|
|
||||||
}
|
|
||||||
payment.difference = payment.closing_amount - payment.expected_amount;
|
payment.difference = payment.closing_amount - payment.expected_amount;
|
||||||
} else {
|
} else {
|
||||||
frm.add_child("payment_reconciliation", {
|
frm.add_child("payment_reconciliation", {
|
||||||
|
|||||||
@@ -1137,6 +1137,45 @@ class TestPricingRule(FrappeTestCase):
|
|||||||
so.save()
|
so.save()
|
||||||
self.assertEqual(len(so.items), 1)
|
self.assertEqual(len(so.items), 1)
|
||||||
|
|
||||||
|
def test_pricing_rule_for_product_free_item_round_free_qty(self):
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
|
||||||
|
test_record = {
|
||||||
|
"doctype": "Pricing Rule",
|
||||||
|
"title": "_Test Pricing Rule",
|
||||||
|
"apply_on": "Item Code",
|
||||||
|
"currency": "USD",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"item_code": "_Test Item",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selling": 1,
|
||||||
|
"rate": 0,
|
||||||
|
"min_qty": 100,
|
||||||
|
"max_qty": 0,
|
||||||
|
"price_or_product_discount": "Product",
|
||||||
|
"same_item": 1,
|
||||||
|
"free_qty": 10,
|
||||||
|
"round_free_qty": 1,
|
||||||
|
"is_recursive": 1,
|
||||||
|
"recurse_for": 100,
|
||||||
|
"company": "_Test Company",
|
||||||
|
}
|
||||||
|
frappe.get_doc(test_record.copy()).insert()
|
||||||
|
|
||||||
|
# With pricing rule
|
||||||
|
so = make_sales_order(item_code="_Test Item", qty=100)
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.items[1].is_free_item, 1)
|
||||||
|
self.assertEqual(so.items[1].item_code, "_Test Item")
|
||||||
|
self.assertEqual(so.items[1].qty, 10)
|
||||||
|
|
||||||
|
so = make_sales_order(item_code="_Test Item", qty=150)
|
||||||
|
so.load_from_db()
|
||||||
|
self.assertEqual(so.items[1].is_free_item, 1)
|
||||||
|
self.assertEqual(so.items[1].item_code, "_Test Item")
|
||||||
|
self.assertEqual(so.items[1].qty, 10)
|
||||||
|
|
||||||
def test_apply_multiple_pricing_rules_for_discount_percentage_and_amount(self):
|
def test_apply_multiple_pricing_rules_for_discount_percentage_and_amount(self):
|
||||||
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
|
||||||
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
|
||||||
|
|||||||
@@ -655,7 +655,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
|||||||
if transaction_qty:
|
if transaction_qty:
|
||||||
qty = flt(transaction_qty) * qty / pricing_rule.recurse_for
|
qty = flt(transaction_qty) * qty / pricing_rule.recurse_for
|
||||||
if pricing_rule.round_free_qty:
|
if pricing_rule.round_free_qty:
|
||||||
qty = math.floor(qty)
|
qty = (flt(transaction_qty) // pricing_rule.recurse_for) * (pricing_rule.free_qty or 1)
|
||||||
|
|
||||||
if not qty:
|
if not qty:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -474,6 +474,7 @@ def send_emails(document_name, from_scheduler=False, posting_date=None):
|
|||||||
reference_doctype="Process Statement Of Accounts",
|
reference_doctype="Process Statement Of Accounts",
|
||||||
reference_name=document_name,
|
reference_name=document_name,
|
||||||
attachments=attachments,
|
attachments=attachments,
|
||||||
|
expose_recipients="header",
|
||||||
)
|
)
|
||||||
|
|
||||||
if doc.enable_auto_email and from_scheduler:
|
if doc.enable_auto_email and from_scheduler:
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class RepostAccountingLedger(Document):
|
|||||||
latest_pcv = (
|
latest_pcv = (
|
||||||
frappe.db.get_all(
|
frappe.db.get_all(
|
||||||
"Period Closing Voucher",
|
"Period Closing Voucher",
|
||||||
filters={"company": self.company},
|
filters={"company": self.company, "docstatus": 1},
|
||||||
order_by="period_end_date desc",
|
order_by="period_end_date desc",
|
||||||
pluck="period_end_date",
|
pluck="period_end_date",
|
||||||
limit=1,
|
limit=1,
|
||||||
|
|||||||
@@ -741,20 +741,6 @@ frappe.ui.form.on("Sales Invoice", {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
frm.set_query("company_address", function (doc) {
|
|
||||||
if (!doc.company) {
|
|
||||||
frappe.throw(__("Please set Company"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
query: "frappe.contacts.doctype.address.address.address_query",
|
|
||||||
filters: {
|
|
||||||
link_doctype: "Company",
|
|
||||||
link_name: doc.company,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
frm.set_query("pos_profile", function (doc) {
|
frm.set_query("pos_profile", function (doc) {
|
||||||
if (!doc.company) {
|
if (!doc.company) {
|
||||||
frappe.throw(__("Please set Company"));
|
frappe.throw(__("Please set Company"));
|
||||||
|
|||||||
@@ -89,11 +89,14 @@
|
|||||||
"incoming_rate",
|
"incoming_rate",
|
||||||
"item_tax_rate",
|
"item_tax_rate",
|
||||||
"actual_batch_qty",
|
"actual_batch_qty",
|
||||||
"actual_qty",
|
|
||||||
"section_break_eoec",
|
"section_break_eoec",
|
||||||
"serial_no",
|
"serial_no",
|
||||||
"column_break_ytgd",
|
"column_break_ytgd",
|
||||||
"batch_no",
|
"batch_no",
|
||||||
|
"available_quantity_section",
|
||||||
|
"actual_qty",
|
||||||
|
"column_break_ogff",
|
||||||
|
"company_total_stock",
|
||||||
"edit_references",
|
"edit_references",
|
||||||
"sales_order",
|
"sales_order",
|
||||||
"so_detail",
|
"so_detail",
|
||||||
@@ -675,7 +678,8 @@
|
|||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "actual_qty",
|
"fieldname": "actual_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Available Qty at Warehouse",
|
"label": "Qty (Warehouse)",
|
||||||
|
"no_copy": 1,
|
||||||
"oldfieldname": "actual_qty",
|
"oldfieldname": "actual_qty",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@@ -923,12 +927,30 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_ytgd",
|
"fieldname": "column_break_ytgd",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "available_quantity_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Available Quantity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_ogff",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "company_total_stock",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Qty (Company)",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-10-28 15:06:40.980995",
|
"modified": "2024-11-25 16:27:33.287341",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class SalesInvoiceItem(Document):
|
|||||||
base_rate_with_margin: DF.Currency
|
base_rate_with_margin: DF.Currency
|
||||||
batch_no: DF.Link | None
|
batch_no: DF.Link | None
|
||||||
brand: DF.Data | None
|
brand: DF.Data | None
|
||||||
|
company_total_stock: DF.Float
|
||||||
conversion_factor: DF.Float
|
conversion_factor: DF.Float
|
||||||
cost_center: DF.Link
|
cost_center: DF.Link
|
||||||
customer_item_code: DF.Data | None
|
customer_item_code: DF.Data | None
|
||||||
|
|||||||
@@ -315,66 +315,48 @@ def check_if_in_list(gle, gl_map):
|
|||||||
|
|
||||||
|
|
||||||
def toggle_debit_credit_if_negative(gl_map):
|
def toggle_debit_credit_if_negative(gl_map):
|
||||||
|
debit_credit_field_map = {
|
||||||
|
"debit": "credit",
|
||||||
|
"debit_in_account_currency": "credit_in_account_currency",
|
||||||
|
"debit_in_transaction_currency": "credit_in_transaction_currency",
|
||||||
|
}
|
||||||
|
|
||||||
for entry in gl_map:
|
for entry in gl_map:
|
||||||
# toggle debit, credit if negative entry
|
# toggle debit, credit if negative entry
|
||||||
if flt(entry.debit) < 0 and flt(entry.credit) < 0 and flt(entry.debit) == flt(entry.credit):
|
for debit_field, credit_field in debit_credit_field_map.items():
|
||||||
entry.credit *= -1
|
debit = flt(entry.get(debit_field))
|
||||||
entry.debit *= -1
|
credit = flt(entry.get(credit_field))
|
||||||
|
|
||||||
if (
|
if debit < 0 and credit < 0 and debit == credit:
|
||||||
flt(entry.debit_in_account_currency) < 0
|
debit *= -1
|
||||||
and flt(entry.credit_in_account_currency) < 0
|
credit *= -1
|
||||||
and flt(entry.debit_in_account_currency) == flt(entry.credit_in_account_currency)
|
|
||||||
):
|
|
||||||
entry.credit_in_account_currency *= -1
|
|
||||||
entry.debit_in_account_currency *= -1
|
|
||||||
|
|
||||||
if flt(entry.debit) < 0:
|
if debit < 0:
|
||||||
entry.credit = flt(entry.credit) - flt(entry.debit)
|
credit = credit - debit
|
||||||
entry.debit = 0.0
|
debit = 0.0
|
||||||
|
|
||||||
if flt(entry.debit_in_account_currency) < 0:
|
if credit < 0:
|
||||||
entry.credit_in_account_currency = flt(entry.credit_in_account_currency) - flt(
|
debit = debit - credit
|
||||||
entry.debit_in_account_currency
|
credit = 0.0
|
||||||
)
|
|
||||||
entry.debit_in_account_currency = 0.0
|
|
||||||
|
|
||||||
if flt(entry.credit) < 0:
|
# update net values
|
||||||
entry.debit = flt(entry.debit) - flt(entry.credit)
|
# In some scenarios net value needs to be shown in the ledger
|
||||||
entry.credit = 0.0
|
# This method updates net values as debit or credit
|
||||||
|
if entry.post_net_value and debit and credit:
|
||||||
|
if debit > credit:
|
||||||
|
debit = debit - credit
|
||||||
|
credit = 0.0
|
||||||
|
|
||||||
if flt(entry.credit_in_account_currency) < 0:
|
else:
|
||||||
entry.debit_in_account_currency = flt(entry.debit_in_account_currency) - flt(
|
credit = credit - debit
|
||||||
entry.credit_in_account_currency
|
debit = 0.0
|
||||||
)
|
|
||||||
entry.credit_in_account_currency = 0.0
|
|
||||||
|
|
||||||
update_net_values(entry)
|
entry[debit_field] = debit
|
||||||
|
entry[credit_field] = credit
|
||||||
|
|
||||||
return gl_map
|
return gl_map
|
||||||
|
|
||||||
|
|
||||||
def update_net_values(entry):
|
|
||||||
# In some scenarios net value needs to be shown in the ledger
|
|
||||||
# This method updates net values as debit or credit
|
|
||||||
if entry.post_net_value and entry.debit and entry.credit:
|
|
||||||
if entry.debit > entry.credit:
|
|
||||||
entry.debit = entry.debit - entry.credit
|
|
||||||
entry.debit_in_account_currency = (
|
|
||||||
entry.debit_in_account_currency - entry.credit_in_account_currency
|
|
||||||
)
|
|
||||||
entry.credit = 0
|
|
||||||
entry.credit_in_account_currency = 0
|
|
||||||
else:
|
|
||||||
entry.credit = entry.credit - entry.debit
|
|
||||||
entry.credit_in_account_currency = (
|
|
||||||
entry.credit_in_account_currency - entry.debit_in_account_currency
|
|
||||||
)
|
|
||||||
|
|
||||||
entry.debit = 0
|
|
||||||
entry.debit_in_account_currency = 0
|
|
||||||
|
|
||||||
|
|
||||||
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
|
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
|
||||||
if not from_repost:
|
if not from_repost:
|
||||||
validate_cwip_accounts(gl_map)
|
validate_cwip_accounts(gl_map)
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Purchase Invoice",
|
"document_type": "Purchase Invoice",
|
||||||
|
"dynamic_filters_json": "[[\"Purchase Invoice\",\"company\",\"=\",\" frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Purchase Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Purchase Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
|
"filters_json": "[[\"Purchase Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Purchase Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
|
||||||
"function": "Sum",
|
"function": "Sum",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_public": 1,
|
"is_public": 1,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Total Incoming Bills",
|
"label": "Total Incoming Bills",
|
||||||
"modified": "2020-07-22 13:06:46.045344",
|
"modified": "2024-11-20 19:08:37.043777",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Total Incoming Bills",
|
"name": "Total Incoming Bills",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Payment Entry",
|
"document_type": "Payment Entry",
|
||||||
|
"dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Receive\",false]]",
|
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Receive\",false]]",
|
||||||
"function": "Sum",
|
"function": "Sum",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Sales Invoice",
|
"document_type": "Sales Invoice",
|
||||||
|
"dynamic_filters_json": "[[\"Sales Invoice\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Sales Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Sales Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
|
"filters_json": "[[\"Sales Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Sales Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
|
||||||
"function": "Sum",
|
"function": "Sum",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Payment Entry",
|
"document_type": "Payment Entry",
|
||||||
|
"dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Pay\",false]]",
|
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Pay\",false]]",
|
||||||
"function": "Sum",
|
"function": "Sum",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -1013,15 +1013,15 @@ class ReceivablePayableReport:
|
|||||||
|
|
||||||
def get_columns(self):
|
def get_columns(self):
|
||||||
self.columns = []
|
self.columns = []
|
||||||
self.add_column("Posting Date", fieldtype="Date")
|
self.add_column(_("Posting Date"), fieldtype="Date")
|
||||||
self.add_column(
|
self.add_column(
|
||||||
label="Party Type",
|
label=_("Party Type"),
|
||||||
fieldname="party_type",
|
fieldname="party_type",
|
||||||
fieldtype="Data",
|
fieldtype="Data",
|
||||||
width=100,
|
width=100,
|
||||||
)
|
)
|
||||||
self.add_column(
|
self.add_column(
|
||||||
label="Party",
|
label=_("Party"),
|
||||||
fieldname="party",
|
fieldname="party",
|
||||||
fieldtype="Dynamic Link",
|
fieldtype="Dynamic Link",
|
||||||
options="party_type",
|
options="party_type",
|
||||||
@@ -1037,10 +1037,10 @@ class ReceivablePayableReport:
|
|||||||
|
|
||||||
if self.party_naming_by == "Naming Series":
|
if self.party_naming_by == "Naming Series":
|
||||||
if self.account_type == "Payable":
|
if self.account_type == "Payable":
|
||||||
label = "Supplier Name"
|
label = _("Supplier Name")
|
||||||
fieldname = "supplier_name"
|
fieldname = "supplier_name"
|
||||||
else:
|
else:
|
||||||
label = "Customer Name"
|
label = _("Customer Name")
|
||||||
fieldname = "customer_name"
|
fieldname = "customer_name"
|
||||||
self.add_column(
|
self.add_column(
|
||||||
label=label,
|
label=label,
|
||||||
@@ -1066,7 +1066,7 @@ class ReceivablePayableReport:
|
|||||||
width=180,
|
width=180,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.add_column(label="Due Date", fieldtype="Date")
|
self.add_column(label=_("Due Date"), fieldtype="Date")
|
||||||
|
|
||||||
if self.account_type == "Payable":
|
if self.account_type == "Payable":
|
||||||
self.add_column(label=_("Bill No"), fieldname="bill_no", fieldtype="Data")
|
self.add_column(label=_("Bill No"), fieldname="bill_no", fieldtype="Data")
|
||||||
|
|||||||
@@ -440,6 +440,7 @@ class GrossProfitGenerator:
|
|||||||
|
|
||||||
if grouped_by_invoice:
|
if grouped_by_invoice:
|
||||||
buying_amount = 0
|
buying_amount = 0
|
||||||
|
base_amount = 0
|
||||||
|
|
||||||
for row in reversed(self.si_list):
|
for row in reversed(self.si_list):
|
||||||
if self.filters.get("group_by") == "Monthly":
|
if self.filters.get("group_by") == "Monthly":
|
||||||
@@ -480,12 +481,11 @@ class GrossProfitGenerator:
|
|||||||
else:
|
else:
|
||||||
row.buying_amount = flt(self.get_buying_amount(row, row.item_code), self.currency_precision)
|
row.buying_amount = flt(self.get_buying_amount(row, row.item_code), self.currency_precision)
|
||||||
|
|
||||||
if grouped_by_invoice:
|
if grouped_by_invoice and row.indent == 0.0:
|
||||||
if row.indent == 1.0:
|
row.buying_amount = buying_amount
|
||||||
buying_amount += row.buying_amount
|
row.base_amount = base_amount
|
||||||
elif row.indent == 0.0:
|
buying_amount = 0
|
||||||
row.buying_amount = buying_amount
|
base_amount = 0
|
||||||
buying_amount = 0
|
|
||||||
|
|
||||||
# get buying rate
|
# get buying rate
|
||||||
if flt(row.qty):
|
if flt(row.qty):
|
||||||
@@ -495,11 +495,19 @@ class GrossProfitGenerator:
|
|||||||
if self.is_not_invoice_row(row):
|
if self.is_not_invoice_row(row):
|
||||||
row.buying_rate, row.base_rate = 0.0, 0.0
|
row.buying_rate, row.base_rate = 0.0, 0.0
|
||||||
|
|
||||||
|
if self.is_not_invoice_row(row):
|
||||||
|
self.update_return_invoices(row)
|
||||||
|
|
||||||
|
if grouped_by_invoice and row.indent == 1.0:
|
||||||
|
buying_amount += row.buying_amount
|
||||||
|
base_amount += row.base_amount
|
||||||
|
|
||||||
# calculate gross profit
|
# calculate gross profit
|
||||||
row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision)
|
row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision)
|
||||||
if row.base_amount:
|
if row.base_amount:
|
||||||
row.gross_profit_percent = flt(
|
row.gross_profit_percent = flt(
|
||||||
(row.gross_profit / row.base_amount) * 100.0, self.currency_precision
|
(row.gross_profit / row.base_amount) * 100.0,
|
||||||
|
self.currency_precision,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
row.gross_profit_percent = 0.0
|
row.gross_profit_percent = 0.0
|
||||||
@@ -510,33 +518,29 @@ class GrossProfitGenerator:
|
|||||||
if self.grouped:
|
if self.grouped:
|
||||||
self.get_average_rate_based_on_group_by()
|
self.get_average_rate_based_on_group_by()
|
||||||
|
|
||||||
|
def update_return_invoices(self, row):
|
||||||
|
if row.parent in self.returned_invoices and row.item_code in self.returned_invoices[row.parent]:
|
||||||
|
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
|
||||||
|
for returned_item_row in returned_item_rows:
|
||||||
|
# returned_items 'qty' should be stateful
|
||||||
|
if returned_item_row.qty != 0:
|
||||||
|
if row.qty >= abs(returned_item_row.qty):
|
||||||
|
row.qty += returned_item_row.qty
|
||||||
|
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
|
||||||
|
returned_item_row.qty = 0
|
||||||
|
returned_item_row.base_amount = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
row.qty = 0
|
||||||
|
row.base_amount = 0
|
||||||
|
returned_item_row.qty += row.qty
|
||||||
|
returned_item_row.base_amount += row.base_amount
|
||||||
|
|
||||||
|
row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
|
||||||
|
|
||||||
def get_average_rate_based_on_group_by(self):
|
def get_average_rate_based_on_group_by(self):
|
||||||
for key in list(self.grouped):
|
for key in list(self.grouped):
|
||||||
if self.filters.get("group_by") == "Invoice":
|
if self.filters.get("group_by") == "Payment Term":
|
||||||
for row in self.grouped[key]:
|
|
||||||
if row.indent == 1.0:
|
|
||||||
if (
|
|
||||||
row.parent in self.returned_invoices
|
|
||||||
and row.item_code in self.returned_invoices[row.parent]
|
|
||||||
):
|
|
||||||
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
|
|
||||||
for returned_item_row in returned_item_rows:
|
|
||||||
# returned_items 'qty' should be stateful
|
|
||||||
if returned_item_row.qty != 0:
|
|
||||||
if row.qty >= abs(returned_item_row.qty):
|
|
||||||
row.qty += returned_item_row.qty
|
|
||||||
returned_item_row.qty = 0
|
|
||||||
else:
|
|
||||||
row.qty = 0
|
|
||||||
returned_item_row.qty += row.qty
|
|
||||||
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
|
|
||||||
row.buying_amount = flt(
|
|
||||||
flt(row.qty) * flt(row.buying_rate), self.currency_precision
|
|
||||||
)
|
|
||||||
if flt(row.qty) or row.base_amount:
|
|
||||||
row = self.set_average_rate(row)
|
|
||||||
self.grouped_data.append(row)
|
|
||||||
elif self.filters.get("group_by") == "Payment Term":
|
|
||||||
for i, row in enumerate(self.grouped[key]):
|
for i, row in enumerate(self.grouped[key]):
|
||||||
invoice_portion = 0
|
invoice_portion = 0
|
||||||
|
|
||||||
@@ -556,7 +560,7 @@ class GrossProfitGenerator:
|
|||||||
|
|
||||||
new_row = self.set_average_rate(new_row)
|
new_row = self.set_average_rate(new_row)
|
||||||
self.grouped_data.append(new_row)
|
self.grouped_data.append(new_row)
|
||||||
else:
|
elif self.filters.get("group_by") != "Invoice":
|
||||||
for i, row in enumerate(self.grouped[key]):
|
for i, row in enumerate(self.grouped[key]):
|
||||||
if i == 0:
|
if i == 0:
|
||||||
new_row = row
|
new_row = row
|
||||||
|
|||||||
@@ -418,12 +418,12 @@ class TestGrossProfit(FrappeTestCase):
|
|||||||
"item_name": self.item,
|
"item_name": self.item,
|
||||||
"warehouse": "Stores - _GP",
|
"warehouse": "Stores - _GP",
|
||||||
"qty": 0.0,
|
"qty": 0.0,
|
||||||
"avg._selling_rate": 0.0,
|
"avg._selling_rate": 100,
|
||||||
"valuation_rate": 0.0,
|
"valuation_rate": 0.0,
|
||||||
"selling_amount": -100.0,
|
"selling_amount": 0.0,
|
||||||
"buying_amount": 0.0,
|
"buying_amount": 0.0,
|
||||||
"gross_profit": -100.0,
|
"gross_profit": 0.0,
|
||||||
"gross_profit_%": 100.0,
|
"gross_profit_%": 0.0,
|
||||||
}
|
}
|
||||||
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
|
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
|
||||||
# Both items of Invoice should have '0' qty
|
# Both items of Invoice should have '0' qty
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Asset",
|
"document_type": "Asset",
|
||||||
|
"dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[]",
|
"filters_json": "[]",
|
||||||
"function": "Sum",
|
"function": "Sum",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Asset",
|
"document_type": "Asset",
|
||||||
|
"dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Asset\",\"creation\",\"Timespan\",\"this year\",false]]",
|
"filters_json": "[[\"Asset\",\"creation\",\"Timespan\",\"this year\",false]]",
|
||||||
"function": "Count",
|
"function": "Count",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Asset",
|
"document_type": "Asset",
|
||||||
|
"dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[]",
|
"filters_json": "[]",
|
||||||
"function": "Count",
|
"function": "Count",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ frappe.ui.form.on("Purchase Order", {
|
|||||||
get_materials_from_supplier: function (frm) {
|
get_materials_from_supplier: function (frm) {
|
||||||
let po_details = [];
|
let po_details = [];
|
||||||
|
|
||||||
if (frm.doc.supplied_items && (flt(frm.doc.per_received, 2) == 100 || frm.doc.status === "Closed")) {
|
if (frm.doc.supplied_items && (flt(frm.doc.per_received) == 100 || frm.doc.status === "Closed")) {
|
||||||
frm.doc.supplied_items.forEach((d) => {
|
frm.doc.supplied_items.forEach((d) => {
|
||||||
if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
|
if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
|
||||||
po_details.push(d.name);
|
po_details.push(d.name);
|
||||||
@@ -329,8 +329,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
|||||||
if (!["Closed", "Delivered"].includes(doc.status)) {
|
if (!["Closed", "Delivered"].includes(doc.status)) {
|
||||||
if (
|
if (
|
||||||
this.frm.doc.status !== "Closed" &&
|
this.frm.doc.status !== "Closed" &&
|
||||||
flt(this.frm.doc.per_received, 2) < 100 &&
|
flt(this.frm.doc.per_received) < 100 &&
|
||||||
flt(this.frm.doc.per_billed, 2) < 100
|
flt(this.frm.doc.per_billed) < 100
|
||||||
) {
|
) {
|
||||||
if (!this.frm.doc.__onload || this.frm.doc.__onload.can_update_items) {
|
if (!this.frm.doc.__onload || this.frm.doc.__onload.can_update_items) {
|
||||||
this.frm.add_custom_button(__("Update Items"), () => {
|
this.frm.add_custom_button(__("Update Items"), () => {
|
||||||
@@ -344,7 +344,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.frm.has_perm("submit")) {
|
if (this.frm.has_perm("submit")) {
|
||||||
if (flt(doc.per_billed, 2) < 100 || flt(doc.per_received, 2) < 100) {
|
if (flt(doc.per_billed) < 100 || flt(doc.per_received) < 100) {
|
||||||
if (doc.status != "On Hold") {
|
if (doc.status != "On Hold") {
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Hold"),
|
__("Hold"),
|
||||||
@@ -383,7 +383,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
|||||||
if (doc.status != "Closed") {
|
if (doc.status != "Closed") {
|
||||||
if (doc.status != "On Hold") {
|
if (doc.status != "On Hold") {
|
||||||
if (flt(doc.per_received) < 100 && allow_receipt) {
|
if (flt(doc.per_received) < 100 && allow_receipt) {
|
||||||
cur_frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Purchase Receipt"),
|
__("Purchase Receipt"),
|
||||||
this.make_purchase_receipt,
|
this.make_purchase_receipt,
|
||||||
__("Create")
|
__("Create")
|
||||||
@@ -408,14 +408,15 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Please do not add precision in the below flt function
|
||||||
if (flt(doc.per_billed) < 100)
|
if (flt(doc.per_billed) < 100)
|
||||||
cur_frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Purchase Invoice"),
|
__("Purchase Invoice"),
|
||||||
this.make_purchase_invoice,
|
this.make_purchase_invoice,
|
||||||
__("Create")
|
__("Create")
|
||||||
);
|
);
|
||||||
|
|
||||||
if (flt(doc.per_billed, 2) < 100 && doc.status != "Delivered") {
|
if (flt(doc.per_billed) < 100 && doc.status != "Delivered") {
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Payment"),
|
__("Payment"),
|
||||||
() => this.make_payment_entry(),
|
() => this.make_payment_entry(),
|
||||||
@@ -423,7 +424,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flt(doc.per_billed, 2) < 100) {
|
if (flt(doc.per_billed) < 100) {
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Payment Request"),
|
__("Payment Request"),
|
||||||
function () {
|
function () {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ def execute(filters=None):
|
|||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
data = get_data(filters)
|
data = get_data(filters)
|
||||||
|
update_received_amount(data)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return [], [], None, []
|
return [], [], None, []
|
||||||
@@ -40,7 +41,6 @@ def get_data(filters):
|
|||||||
po = frappe.qb.DocType("Purchase Order")
|
po = frappe.qb.DocType("Purchase Order")
|
||||||
po_item = frappe.qb.DocType("Purchase Order Item")
|
po_item = frappe.qb.DocType("Purchase Order Item")
|
||||||
pi_item = frappe.qb.DocType("Purchase Invoice Item")
|
pi_item = frappe.qb.DocType("Purchase Invoice Item")
|
||||||
pr_item = frappe.qb.DocType("Purchase Receipt Item")
|
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
frappe.qb.from_(po)
|
frappe.qb.from_(po)
|
||||||
@@ -48,8 +48,6 @@ def get_data(filters):
|
|||||||
.on(po_item.parent == po.name)
|
.on(po_item.parent == po.name)
|
||||||
.left_join(pi_item)
|
.left_join(pi_item)
|
||||||
.on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1))
|
.on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1))
|
||||||
.left_join(pr_item)
|
|
||||||
.on((pr_item.purchase_order_item == po_item.name) & (pr_item.docstatus == 1))
|
|
||||||
.select(
|
.select(
|
||||||
po.transaction_date.as_("date"),
|
po.transaction_date.as_("date"),
|
||||||
po_item.schedule_date.as_("required_date"),
|
po_item.schedule_date.as_("required_date"),
|
||||||
@@ -63,7 +61,6 @@ def get_data(filters):
|
|||||||
(po_item.qty - po_item.received_qty).as_("pending_qty"),
|
(po_item.qty - po_item.received_qty).as_("pending_qty"),
|
||||||
Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"),
|
Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"),
|
||||||
po_item.base_amount.as_("amount"),
|
po_item.base_amount.as_("amount"),
|
||||||
(pr_item.base_amount).as_("received_qty_amount"),
|
|
||||||
(po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"),
|
(po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"),
|
||||||
(po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_(
|
(po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_(
|
||||||
"pending_amount"
|
"pending_amount"
|
||||||
@@ -95,6 +92,39 @@ def get_data(filters):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def update_received_amount(data):
|
||||||
|
pr_data = get_received_amount_data(data)
|
||||||
|
|
||||||
|
for row in data:
|
||||||
|
row.received_qty_amount = flt(pr_data.get(row.name))
|
||||||
|
|
||||||
|
|
||||||
|
def get_received_amount_data(data):
|
||||||
|
pr = frappe.qb.DocType("Purchase Receipt")
|
||||||
|
pr_item = frappe.qb.DocType("Purchase Receipt Item")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(pr)
|
||||||
|
.inner_join(pr_item)
|
||||||
|
.on(pr_item.parent == pr.name)
|
||||||
|
.select(
|
||||||
|
pr_item.purchase_order_item,
|
||||||
|
Sum(pr_item.base_amount).as_("received_qty_amount"),
|
||||||
|
)
|
||||||
|
.where((pr_item.parent == pr.name) & (pr.docstatus == 1))
|
||||||
|
.groupby(pr_item.purchase_order_item)
|
||||||
|
)
|
||||||
|
|
||||||
|
query = query.where(pr_item.purchase_order_item.isin([row.name for row in data]))
|
||||||
|
|
||||||
|
data = query.run()
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
return frappe._dict()
|
||||||
|
|
||||||
|
return frappe._dict(data)
|
||||||
|
|
||||||
|
|
||||||
def prepare_data(data, filters):
|
def prepare_data(data, filters):
|
||||||
completed, pending = 0, 0
|
completed, pending = 0, 0
|
||||||
pending_field = "pending_amount"
|
pending_field = "pending_amount"
|
||||||
|
|||||||
@@ -1036,7 +1036,7 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field
|
|||||||
available_serial_nos.append(serial_no)
|
available_serial_nos.append(serial_no)
|
||||||
|
|
||||||
if available_serial_nos:
|
if available_serial_nos:
|
||||||
if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]:
|
if parent_doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
|
||||||
available_serial_nos = get_available_serial_nos(available_serial_nos, warehouse)
|
available_serial_nos = get_available_serial_nos(available_serial_nos, warehouse)
|
||||||
|
|
||||||
if len(available_serial_nos) > qty:
|
if len(available_serial_nos) > qty:
|
||||||
@@ -1052,7 +1052,7 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field
|
|||||||
if batch_qty <= 0:
|
if batch_qty <= 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]:
|
if parent_doc.doctype in ["Purchase Invoice", "Purchase Receipt"]:
|
||||||
batch_qty = get_available_batch_qty(
|
batch_qty = get_available_batch_qty(
|
||||||
parent_doc,
|
parent_doc,
|
||||||
batch_no,
|
batch_no,
|
||||||
|
|||||||
@@ -21,9 +21,15 @@ class SellingController(StockController):
|
|||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
super().onload()
|
super().onload()
|
||||||
if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice"):
|
if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice", "Quotation"):
|
||||||
for item in self.get("items") + (self.get("packed_items") or []):
|
for item in self.get("items") + (self.get("packed_items") or []):
|
||||||
item.update(get_bin_details(item.item_code, item.warehouse, include_child_warehouses=True))
|
company = self.company
|
||||||
|
|
||||||
|
item.update(
|
||||||
|
get_bin_details(
|
||||||
|
item.item_code, item.warehouse, company=company, include_child_warehouses=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super().validate()
|
super().validate()
|
||||||
|
|||||||
@@ -1008,11 +1008,13 @@ class StockController(AccountsController):
|
|||||||
def validate_qi_presence(self, row):
|
def validate_qi_presence(self, row):
|
||||||
"""Check if QI is present on row level. Warn on save and stop on submit if missing."""
|
"""Check if QI is present on row level. Warn on save and stop on submit if missing."""
|
||||||
if not row.quality_inspection:
|
if not row.quality_inspection:
|
||||||
msg = f"Row #{row.idx}: Quality Inspection is required for Item {frappe.bold(row.item_code)}"
|
msg = _("Row #{0}: Quality Inspection is required for Item {1}").format(
|
||||||
|
row.idx, frappe.bold(row.item_code)
|
||||||
|
)
|
||||||
if self.docstatus == 1:
|
if self.docstatus == 1:
|
||||||
frappe.throw(_(msg), title=_("Inspection Required"), exc=QualityInspectionRequiredError)
|
frappe.throw(msg, title=_("Inspection Required"), exc=QualityInspectionRequiredError)
|
||||||
else:
|
else:
|
||||||
frappe.msgprint(_(msg), title=_("Inspection Required"), indicator="blue")
|
frappe.msgprint(msg, title=_("Inspection Required"), indicator="blue")
|
||||||
|
|
||||||
def validate_qi_submission(self, row):
|
def validate_qi_submission(self, row):
|
||||||
"""Check if QI is submitted on row level, during submission"""
|
"""Check if QI is submitted on row level, during submission"""
|
||||||
@@ -1021,11 +1023,13 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
if not qa_docstatus == 1:
|
if not qa_docstatus == 1:
|
||||||
link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection)
|
link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection)
|
||||||
msg = f"Row #{row.idx}: Quality Inspection {link} is not submitted for the item: {row.item_code}"
|
msg = _("Row #{0}: Quality Inspection {1} is not submitted for the item: {2}").format(
|
||||||
|
row.idx, link, row.item_code
|
||||||
|
)
|
||||||
if action == "Stop":
|
if action == "Stop":
|
||||||
frappe.throw(_(msg), title=_("Inspection Submission"), exc=QualityInspectionNotSubmittedError)
|
frappe.throw(msg, title=_("Inspection Submission"), exc=QualityInspectionNotSubmittedError)
|
||||||
else:
|
else:
|
||||||
frappe.msgprint(_(msg), alert=True, indicator="orange")
|
frappe.msgprint(msg, alert=True, indicator="orange")
|
||||||
|
|
||||||
def validate_qi_rejection(self, row):
|
def validate_qi_rejection(self, row):
|
||||||
"""Check if QI is rejected on row level, during submission"""
|
"""Check if QI is rejected on row level, during submission"""
|
||||||
@@ -1034,11 +1038,13 @@ class StockController(AccountsController):
|
|||||||
|
|
||||||
if qa_status == "Rejected":
|
if qa_status == "Rejected":
|
||||||
link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection)
|
link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection)
|
||||||
msg = f"Row #{row.idx}: Quality Inspection {link} was rejected for item {row.item_code}"
|
msg = _("Row #{0}: Quality Inspection {1} was rejected for item {2}").format(
|
||||||
|
row.idx, link, row.item_code
|
||||||
|
)
|
||||||
if action == "Stop":
|
if action == "Stop":
|
||||||
frappe.throw(_(msg), title=_("Inspection Rejected"), exc=QualityInspectionRejectedError)
|
frappe.throw(msg, title=_("Inspection Rejected"), exc=QualityInspectionRejectedError)
|
||||||
else:
|
else:
|
||||||
frappe.msgprint(_(msg), alert=True, indicator="orange")
|
frappe.msgprint(msg, alert=True, indicator="orange")
|
||||||
|
|
||||||
def update_blanket_order(self):
|
def update_blanket_order(self):
|
||||||
blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order]))
|
blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order]))
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Opportunity",
|
"document_type": "Opportunity",
|
||||||
"dynamic_filters_json": "[[\"Opportunity\",\"status\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
"dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Opportunity\",\"company\",\"=\",null,false]]",
|
"filters_json": "[[\"Opportunity\",\"company\",\"=\",null,false]]",
|
||||||
"function": "Count",
|
"function": "Count",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"include_item_in_manufacturing",
|
"include_item_in_manufacturing",
|
||||||
"qty_section",
|
"qty_section",
|
||||||
"required_qty",
|
"required_qty",
|
||||||
|
"stock_uom",
|
||||||
"rate",
|
"rate",
|
||||||
"amount",
|
"amount",
|
||||||
"column_break_11",
|
"column_break_11",
|
||||||
@@ -138,11 +139,19 @@
|
|||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Returned Qty ",
|
"label": "Returned Qty ",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "item_code.stock_uom",
|
||||||
|
"fieldname": "stock_uom",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Stock UOM",
|
||||||
|
"options": "UOM",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-02-11 15:45:32.318374",
|
"modified": "2024-11-19 15:48:16.823384",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Work Order Item",
|
"name": "Work Order Item",
|
||||||
@@ -153,4 +162,4 @@
|
|||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class WorkOrderItem(Document):
|
|||||||
item_code: DF.Link | None
|
item_code: DF.Link | None
|
||||||
item_name: DF.Data | None
|
item_name: DF.Data | None
|
||||||
operation: DF.Link | None
|
operation: DF.Link | None
|
||||||
|
operation_row_id: DF.Int
|
||||||
parent: DF.Data
|
parent: DF.Data
|
||||||
parentfield: DF.Data
|
parentfield: DF.Data
|
||||||
parenttype: DF.Data
|
parenttype: DF.Data
|
||||||
@@ -32,6 +33,7 @@ class WorkOrderItem(Document):
|
|||||||
required_qty: DF.Float
|
required_qty: DF.Float
|
||||||
returned_qty: DF.Float
|
returned_qty: DF.Float
|
||||||
source_warehouse: DF.Link | None
|
source_warehouse: DF.Link | None
|
||||||
|
stock_uom: DF.Link | None
|
||||||
transferred_qty: DF.Float
|
transferred_qty: DF.Float
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Work Order",
|
"document_type": "Work Order",
|
||||||
|
"dynamic_filters_json": "[[\"Work Order\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Work Order\",\"status\",\"=\",\"Completed\"],[\"Work Order\",\"docstatus\",\"=\",1],[\"Work Order\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]",
|
"filters_json": "[[\"Work Order\",\"status\",\"=\",\"Completed\"],[\"Work Order\",\"docstatus\",\"=\",1],[\"Work Order\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]",
|
||||||
"function": "Count",
|
"function": "Count",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Work Order",
|
"document_type": "Work Order",
|
||||||
|
"dynamic_filters_json": "[[\"Work Order\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Work Order\",\"docstatus\",\"=\",1],[\"Work Order\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]",
|
"filters_json": "[[\"Work Order\",\"docstatus\",\"=\",1],[\"Work Order\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]",
|
||||||
"function": "Count",
|
"function": "Count",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Job Card",
|
"document_type": "Job Card",
|
||||||
|
"dynamic_filters_json": "[[\"Job Card\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Job Card\",\"status\",\"!=\",\"Completed\"],[\"Job Card\",\"docstatus\",\"=\",1]]",
|
"filters_json": "[[\"Job Card\",\"status\",\"!=\",\"Completed\"],[\"Job Card\",\"docstatus\",\"=\",1]]",
|
||||||
"function": "Count",
|
"function": "Count",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -382,3 +382,4 @@ erpnext.patches.v15_0.link_purchase_item_to_asset_doc
|
|||||||
erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter
|
erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter
|
||||||
erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log
|
erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log
|
||||||
erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries
|
erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries
|
||||||
|
erpnext.patches.v14_0.update_stock_uom_in_work_order_item
|
||||||
|
|||||||
15
erpnext/patches/v14_0/update_stock_uom_in_work_order_item.py
Normal file
15
erpnext/patches/v14_0/update_stock_uom_in_work_order_item.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.db.sql(
|
||||||
|
"""
|
||||||
|
UPDATE
|
||||||
|
`tabWork Order Item`, `tabItem`
|
||||||
|
SET
|
||||||
|
`tabWork Order Item`.stock_uom = `tabItem`.stock_uom
|
||||||
|
WHERE
|
||||||
|
`tabWork Order Item`.item_code = `tabItem`.name
|
||||||
|
AND `tabWork Order Item`.docstatus = 1
|
||||||
|
"""
|
||||||
|
)
|
||||||
@@ -1904,8 +1904,14 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (!r.exc) {
|
if (!r.exc) {
|
||||||
frappe.run_serially([
|
frappe.run_serially([
|
||||||
() => me.frm.set_value("price_list_currency", r.message.parent.price_list_currency),
|
() => {
|
||||||
() => me.frm.set_value("plc_conversion_rate", r.message.parent.plc_conversion_rate),
|
if (r.message.parent.price_list_currency)
|
||||||
|
me.frm.set_value("price_list_currency", r.message.parent.price_list_currency);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (r.message.parent.plc_conversion_rate)
|
||||||
|
me.frm.set_value("plc_conversion_rate", r.message.parent.plc_conversion_rate);
|
||||||
|
},
|
||||||
() => {
|
() => {
|
||||||
if(args.items.length) {
|
if(args.items.length) {
|
||||||
me._set_values_for_item_list(r.message.children);
|
me._set_values_for_item_list(r.message.children);
|
||||||
|
|||||||
@@ -77,9 +77,13 @@ $.extend(erpnext.queries, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
company_address_query: function (doc) {
|
company_address_query: function (doc) {
|
||||||
|
if (!doc.company) {
|
||||||
|
frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "company", doc.name))]));
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
query: "frappe.contacts.doctype.address.address.address_query",
|
query: "frappe.contacts.doctype.address.address.address_query",
|
||||||
filters: { is_your_company_address: 1, link_doctype: "Company", link_name: doc.company || "" },
|
filters: { link_doctype: "Company", link_name: doc.company },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ erpnext.sales_common = {
|
|||||||
me.frm.set_query("customer_address", erpnext.queries.address_query);
|
me.frm.set_query("customer_address", erpnext.queries.address_query);
|
||||||
me.frm.set_query("shipping_address_name", erpnext.queries.address_query);
|
me.frm.set_query("shipping_address_name", erpnext.queries.address_query);
|
||||||
me.frm.set_query("dispatch_address_name", erpnext.queries.dispatch_address_query);
|
me.frm.set_query("dispatch_address_name", erpnext.queries.dispatch_address_query);
|
||||||
|
me.frm.set_query("company_address", erpnext.queries.company_address_query);
|
||||||
|
|
||||||
erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
|
erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.query_builder import Criterion
|
||||||
from frappe.utils import get_link_to_form
|
from frappe.utils import get_link_to_form
|
||||||
|
|
||||||
|
|
||||||
@@ -93,15 +94,24 @@ class ProductBundle(Document):
|
|||||||
def get_new_item_code(doctype, txt, searchfield, start, page_len, filters):
|
def get_new_item_code(doctype, txt, searchfield, start, page_len, filters):
|
||||||
product_bundles = frappe.db.get_list("Product Bundle", {"disabled": 0}, pluck="name")
|
product_bundles = frappe.db.get_list("Product Bundle", {"disabled": 0}, pluck="name")
|
||||||
|
|
||||||
|
if not searchfield or searchfield == "name":
|
||||||
|
searchfield = frappe.get_meta("Item").get("search_fields")
|
||||||
|
|
||||||
|
searchfield = searchfield.split(",")
|
||||||
|
searchfield.append("name")
|
||||||
|
|
||||||
item = frappe.qb.DocType("Item")
|
item = frappe.qb.DocType("Item")
|
||||||
query = (
|
query = (
|
||||||
frappe.qb.from_(item)
|
frappe.qb.from_(item)
|
||||||
.select(item.item_code, item.item_name)
|
.select(item.name, item.item_name)
|
||||||
.where((item.is_stock_item == 0) & (item.is_fixed_asset == 0) & (item[searchfield].like(f"%{txt}%")))
|
.where((item.is_stock_item == 0) & (item.is_fixed_asset == 0))
|
||||||
.limit(page_len)
|
.limit(page_len)
|
||||||
.offset(start)
|
.offset(start)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if searchfield:
|
||||||
|
query = query.where(Criterion.any([item[fieldname].like(f"%{txt}%") for fieldname in searchfield]))
|
||||||
|
|
||||||
if product_bundles:
|
if product_bundles:
|
||||||
query = query.where(item.name.notin(product_bundles))
|
query = query.where(item.name.notin(product_bundles))
|
||||||
|
|
||||||
|
|||||||
@@ -24,20 +24,6 @@ frappe.ui.form.on("Quotation", {
|
|||||||
frm.set_df_property("packed_items", "cannot_add_rows", true);
|
frm.set_df_property("packed_items", "cannot_add_rows", true);
|
||||||
frm.set_df_property("packed_items", "cannot_delete_rows", true);
|
frm.set_df_property("packed_items", "cannot_delete_rows", true);
|
||||||
|
|
||||||
frm.set_query("company_address", function (doc) {
|
|
||||||
if (!doc.company) {
|
|
||||||
frappe.throw(__("Please set Company"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
query: "frappe.contacts.doctype.address.address.address_query",
|
|
||||||
filters: {
|
|
||||||
link_doctype: "Company",
|
|
||||||
link_name: doc.company,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
frm.set_query("serial_and_batch_bundle", "packed_items", (doc, cdt, cdn) => {
|
frm.set_query("serial_and_batch_bundle", "packed_items", (doc, cdt, cdn) => {
|
||||||
let row = locals[cdt][cdn];
|
let row = locals[cdt][cdn];
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -24,6 +24,10 @@
|
|||||||
"uom",
|
"uom",
|
||||||
"conversion_factor",
|
"conversion_factor",
|
||||||
"stock_qty",
|
"stock_qty",
|
||||||
|
"available_quantity_section",
|
||||||
|
"actual_qty",
|
||||||
|
"column_break_ylrv",
|
||||||
|
"company_total_stock",
|
||||||
"section_break_16",
|
"section_break_16",
|
||||||
"price_list_rate",
|
"price_list_rate",
|
||||||
"base_price_list_rate",
|
"base_price_list_rate",
|
||||||
@@ -70,7 +74,6 @@
|
|||||||
"prevdoc_docname",
|
"prevdoc_docname",
|
||||||
"item_balance",
|
"item_balance",
|
||||||
"projected_qty",
|
"projected_qty",
|
||||||
"actual_qty",
|
|
||||||
"col_break4",
|
"col_break4",
|
||||||
"stock_balance",
|
"stock_balance",
|
||||||
"item_tax_rate",
|
"item_tax_rate",
|
||||||
@@ -460,9 +463,10 @@
|
|||||||
"report_hide": 1
|
"report_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
"fieldname": "actual_qty",
|
"fieldname": "actual_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Actual Qty",
|
"label": "Qty (Warehouse)",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
@@ -662,12 +666,31 @@
|
|||||||
"label": "Has Alternative Item",
|
"label": "Has Alternative Item",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "available_quantity_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Available Quantity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_ylrv",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "company_total_stock",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Qty (Company)",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1,
|
||||||
|
"report_hide": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-11-14 18:24:24.619832",
|
"modified": "2024-11-24 15:18:43.952844",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Quotation Item",
|
"name": "Quotation Item",
|
||||||
@@ -677,4 +700,4 @@
|
|||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class QuotationItem(Document):
|
|||||||
blanket_order: DF.Link | None
|
blanket_order: DF.Link | None
|
||||||
blanket_order_rate: DF.Currency
|
blanket_order_rate: DF.Currency
|
||||||
brand: DF.Link | None
|
brand: DF.Link | None
|
||||||
|
company_total_stock: DF.Float
|
||||||
conversion_factor: DF.Float
|
conversion_factor: DF.Float
|
||||||
customer_item_code: DF.Data | None
|
customer_item_code: DF.Data | None
|
||||||
description: DF.TextEditor | None
|
description: DF.TextEditor | None
|
||||||
|
|||||||
@@ -26,20 +26,6 @@ frappe.ui.form.on("Sales Order", {
|
|||||||
return doc.stock_qty <= doc.delivered_qty ? "green" : "orange";
|
return doc.stock_qty <= doc.delivered_qty ? "green" : "orange";
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("company_address", function (doc) {
|
|
||||||
if (!doc.company) {
|
|
||||||
frappe.throw(__("Please set Company"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
query: "frappe.contacts.doctype.address.address.address_query",
|
|
||||||
filters: {
|
|
||||||
link_doctype: "Company",
|
|
||||||
link_name: doc.company,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
frm.set_query("bom_no", "items", function (doc, cdt, cdn) {
|
frm.set_query("bom_no", "items", function (doc, cdt, cdn) {
|
||||||
var row = locals[cdt][cdn];
|
var row = locals[cdt][cdn];
|
||||||
return {
|
return {
|
||||||
@@ -57,8 +43,8 @@ frappe.ui.form.on("Sales Order", {
|
|||||||
if (frm.doc.docstatus === 1) {
|
if (frm.doc.docstatus === 1) {
|
||||||
if (
|
if (
|
||||||
frm.doc.status !== "Closed" &&
|
frm.doc.status !== "Closed" &&
|
||||||
flt(frm.doc.per_delivered, 2) < 100 &&
|
flt(frm.doc.per_delivered) < 100 &&
|
||||||
flt(frm.doc.per_billed, 2) < 100 &&
|
flt(frm.doc.per_billed) < 100 &&
|
||||||
frm.has_perm("write")
|
frm.has_perm("write")
|
||||||
) {
|
) {
|
||||||
frm.add_custom_button(__("Update Items"), () => {
|
frm.add_custom_button(__("Update Items"), () => {
|
||||||
@@ -604,7 +590,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
|||||||
__("Status")
|
__("Status")
|
||||||
);
|
);
|
||||||
|
|
||||||
if (flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed, 2) < 100) {
|
if (flt(doc.per_delivered) < 100 || flt(doc.per_billed) < 100) {
|
||||||
// close
|
// close
|
||||||
this.frm.add_custom_button(__("Close"), () => this.close_sales_order(), __("Status"));
|
this.frm.add_custom_button(__("Close"), () => this.close_sales_order(), __("Status"));
|
||||||
}
|
}
|
||||||
@@ -627,7 +613,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
|||||||
) && !this.frm.doc.skip_delivery_note;
|
) && !this.frm.doc.skip_delivery_note;
|
||||||
|
|
||||||
if (this.frm.has_perm("submit")) {
|
if (this.frm.has_perm("submit")) {
|
||||||
if (flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed, 2) < 100) {
|
if (flt(doc.per_delivered) < 100 || flt(doc.per_billed) < 100) {
|
||||||
// hold
|
// hold
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Hold"),
|
__("Hold"),
|
||||||
@@ -645,8 +631,8 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
(!doc.__onload || !doc.__onload.has_reserved_stock) &&
|
(!doc.__onload || !doc.__onload.has_reserved_stock) &&
|
||||||
flt(doc.per_picked, 2) < 100 &&
|
flt(doc.per_picked) < 100 &&
|
||||||
flt(doc.per_delivered, 2) < 100 &&
|
flt(doc.per_delivered) < 100 &&
|
||||||
frappe.model.can_create("Pick List")
|
frappe.model.can_create("Pick List")
|
||||||
) {
|
) {
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
@@ -664,7 +650,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
|||||||
|
|
||||||
// delivery note
|
// delivery note
|
||||||
if (
|
if (
|
||||||
flt(doc.per_delivered, 2) < 100 &&
|
flt(doc.per_delivered) < 100 &&
|
||||||
(order_is_a_sale || order_is_a_custom_sale) &&
|
(order_is_a_sale || order_is_a_custom_sale) &&
|
||||||
allow_delivery
|
allow_delivery
|
||||||
) {
|
) {
|
||||||
@@ -686,7 +672,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sales invoice
|
// sales invoice
|
||||||
if (flt(doc.per_billed, 2) < 100 && frappe.model.can_create("Sales Invoice")) {
|
if (flt(doc.per_billed) < 100 && frappe.model.can_create("Sales Invoice")) {
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Sales Invoice"),
|
__("Sales Invoice"),
|
||||||
() => me.make_sales_invoice(),
|
() => me.make_sales_invoice(),
|
||||||
@@ -697,8 +683,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
|||||||
// material request
|
// material request
|
||||||
if (
|
if (
|
||||||
(!doc.order_type ||
|
(!doc.order_type ||
|
||||||
((order_is_a_sale || order_is_a_custom_sale) &&
|
((order_is_a_sale || order_is_a_custom_sale) && flt(doc.per_delivered) < 100)) &&
|
||||||
flt(doc.per_delivered, 2) < 100)) &&
|
|
||||||
frappe.model.can_create("Material Request")
|
frappe.model.can_create("Material Request")
|
||||||
) {
|
) {
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
@@ -723,7 +708,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// maintenance
|
// maintenance
|
||||||
if (flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) {
|
if (flt(doc.per_delivered) < 100 && (order_is_maintenance || order_is_a_custom_sale)) {
|
||||||
if (frappe.model.can_create("Maintenance Visit")) {
|
if (frappe.model.can_create("Maintenance Visit")) {
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Maintenance Visit"),
|
__("Maintenance Visit"),
|
||||||
@@ -741,7 +726,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// project
|
// project
|
||||||
if (flt(doc.per_delivered, 2) < 100 && frappe.model.can_create("Project")) {
|
if (flt(doc.per_delivered) < 100 && frappe.model.can_create("Project")) {
|
||||||
this.frm.add_custom_button(__("Project"), () => this.make_project(), __("Create"));
|
this.frm.add_custom_button(__("Project"), () => this.make_project(), __("Create"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,10 +754,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// payment request
|
// payment request
|
||||||
if (
|
if (flt(doc.per_billed) < 100 + frappe.boot.sysdefaults.over_billing_allowance) {
|
||||||
flt(doc.per_billed, precision("per_billed", doc)) <
|
|
||||||
100 + frappe.boot.sysdefaults.over_billing_allowance
|
|
||||||
) {
|
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Payment Request"),
|
__("Payment Request"),
|
||||||
() => this.make_payment_request(),
|
() => this.make_payment_request(),
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ frappe.listview_settings["Sales Order"] = {
|
|||||||
return [__("On Hold"), "orange", "status,=,On Hold"];
|
return [__("On Hold"), "orange", "status,=,On Hold"];
|
||||||
} else if (doc.status === "Completed") {
|
} else if (doc.status === "Completed") {
|
||||||
return [__("Completed"), "green", "status,=,Completed"];
|
return [__("Completed"), "green", "status,=,Completed"];
|
||||||
} else if (!doc.skip_delivery_note && flt(doc.per_delivered, 2) < 100) {
|
} else if (!doc.skip_delivery_note && flt(doc.per_delivered) < 100) {
|
||||||
if (frappe.datetime.get_diff(doc.delivery_date) < 0) {
|
if (frappe.datetime.get_diff(doc.delivery_date) < 0) {
|
||||||
// not delivered & overdue
|
// not delivered & overdue
|
||||||
return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"];
|
return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"];
|
||||||
} else if (flt(doc.grand_total) === 0) {
|
} else if (flt(doc.grand_total) === 0) {
|
||||||
// not delivered (zeroount order)
|
// not delivered (zeroount order)
|
||||||
return [__("To Deliver"), "orange", "per_delivered,<,100|grand_total,=,0|status,!=,Closed"];
|
return [__("To Deliver"), "orange", "per_delivered,<,100|grand_total,=,0|status,!=,Closed"];
|
||||||
} else if (flt(doc.per_billed, 2) < 100) {
|
} else if (flt(doc.per_billed) < 100) {
|
||||||
// not delivered & not billed
|
// not delivered & not billed
|
||||||
return [
|
return [
|
||||||
__("To Deliver and Bill"),
|
__("To Deliver and Bill"),
|
||||||
@@ -39,13 +39,13 @@ frappe.listview_settings["Sales Order"] = {
|
|||||||
return [__("To Deliver"), "orange", "per_delivered,<,100|per_billed,=,100|status,!=,Closed"];
|
return [__("To Deliver"), "orange", "per_delivered,<,100|per_billed,=,100|status,!=,Closed"];
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
flt(doc.per_delivered, 2) === 100 &&
|
flt(doc.per_delivered) === 100 &&
|
||||||
flt(doc.grand_total) !== 0 &&
|
flt(doc.grand_total) !== 0 &&
|
||||||
flt(doc.per_billed, 2) < 100
|
flt(doc.per_billed) < 100
|
||||||
) {
|
) {
|
||||||
// to bill
|
// to bill
|
||||||
return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"];
|
return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"];
|
||||||
} else if (doc.skip_delivery_note && flt(doc.per_billed, 2) < 100) {
|
} else if (doc.skip_delivery_note && flt(doc.per_billed) < 100) {
|
||||||
return [__("To Bill"), "orange", "per_billed,<,100|status,!=,Closed"];
|
return [__("To Bill"), "orange", "per_billed,<,100|status,!=,Closed"];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -78,11 +78,14 @@
|
|||||||
"against_blanket_order",
|
"against_blanket_order",
|
||||||
"blanket_order",
|
"blanket_order",
|
||||||
"blanket_order_rate",
|
"blanket_order_rate",
|
||||||
|
"available_quantity_section",
|
||||||
|
"actual_qty",
|
||||||
|
"column_break_jpky",
|
||||||
|
"company_total_stock",
|
||||||
"manufacturing_section_section",
|
"manufacturing_section_section",
|
||||||
"bom_no",
|
"bom_no",
|
||||||
"planning_section",
|
"planning_section",
|
||||||
"projected_qty",
|
"projected_qty",
|
||||||
"actual_qty",
|
|
||||||
"ordered_qty",
|
"ordered_qty",
|
||||||
"planned_qty",
|
"planned_qty",
|
||||||
"production_plan_qty",
|
"production_plan_qty",
|
||||||
@@ -636,7 +639,7 @@
|
|||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "actual_qty",
|
"fieldname": "actual_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Actual Qty",
|
"label": "Qty (Warehouse)",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_width": "70px",
|
"print_width": "70px",
|
||||||
@@ -905,12 +908,30 @@
|
|||||||
"label": "Is Stock Item",
|
"label": "Is Stock Item",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"report_hide": 1
|
"report_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "company_total_stock",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Qty (Company)",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_jpky",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "available_quantity_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Available Quantity"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-03-21 18:15:56.625005",
|
"modified": "2024-11-21 14:21:29.743474",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales Order Item",
|
"name": "Sales Order Item",
|
||||||
@@ -921,4 +942,4 @@
|
|||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class SalesOrderItem(Document):
|
|||||||
blanket_order_rate: DF.Currency
|
blanket_order_rate: DF.Currency
|
||||||
bom_no: DF.Link | None
|
bom_no: DF.Link | None
|
||||||
brand: DF.Link | None
|
brand: DF.Link | None
|
||||||
|
company_total_stock: DF.Float
|
||||||
conversion_factor: DF.Float
|
conversion_factor: DF.Float
|
||||||
customer_item_code: DF.Data | None
|
customer_item_code: DF.Data | None
|
||||||
delivered_by_supplier: DF.Check
|
delivered_by_supplier: DF.Check
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Customer",
|
"document_type": "Customer",
|
||||||
"dynamic_filters_json": "",
|
|
||||||
"filters_json": "[[\"Customer\",\"disabled\",\"=\",\"0\"]]",
|
"filters_json": "[[\"Customer\",\"disabled\",\"=\",\"0\"]]",
|
||||||
"function": "Count",
|
"function": "Count",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ def search_by_term(search_term, warehouse, price_list):
|
|||||||
def filter_result_items(result, pos_profile):
|
def filter_result_items(result, pos_profile):
|
||||||
if result and result.get("items"):
|
if result and result.get("items"):
|
||||||
pos_item_groups = frappe.db.get_all("POS Item Group", {"parent": pos_profile}, pluck="item_group")
|
pos_item_groups = frappe.db.get_all("POS Item Group", {"parent": pos_profile}, pluck="item_group")
|
||||||
|
if not pos_item_groups:
|
||||||
|
return
|
||||||
result["items"] = [item for item in result.get("items") if item.get("item_group") in pos_item_groups]
|
result["items"] = [item for item in result.get("items") if item.get("item_group") in pos_item_groups]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -556,7 +556,7 @@ erpnext.PointOfSale.Controller = class {
|
|||||||
const item_row_exists = !$.isEmptyObject(item_row);
|
const item_row_exists = !$.isEmptyObject(item_row);
|
||||||
|
|
||||||
const from_selector = field === "qty" && value === "+1";
|
const from_selector = field === "qty" && value === "+1";
|
||||||
if (from_selector) value = flt(item_row.stock_qty) + flt(value);
|
if (from_selector) value = flt(item_row.qty) + flt(value);
|
||||||
|
|
||||||
if (item_row_exists) {
|
if (item_row_exists) {
|
||||||
if (field === "qty") value = flt(value);
|
if (field === "qty") value = flt(value);
|
||||||
@@ -687,7 +687,7 @@ erpnext.PointOfSale.Controller = class {
|
|||||||
const is_stock_item = resp[1];
|
const is_stock_item = resp[1];
|
||||||
|
|
||||||
frappe.dom.unfreeze();
|
frappe.dom.unfreeze();
|
||||||
const bold_uom = item_row.uom.bold();
|
const bold_uom = item_row.stock_uom.bold();
|
||||||
const bold_item_code = item_row.item_code.bold();
|
const bold_item_code = item_row.item_code.bold();
|
||||||
const bold_warehouse = warehouse.bold();
|
const bold_warehouse = warehouse.bold();
|
||||||
const bold_available_qty = available_qty.toString().bold();
|
const bold_available_qty = available_qty.toString().bold();
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ frappe.query_reports["Sales Analytics"] = {
|
|||||||
default: "Monthly",
|
default: "Monthly",
|
||||||
reqd: 1,
|
reqd: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldname: "show_aggregate_value_from_subsidiary_companies",
|
||||||
|
label: __("Show Aggregate Value from Subsidiary Companies"),
|
||||||
|
fieldtype: "Check",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
get_datatable_options(options) {
|
get_datatable_options(options) {
|
||||||
return Object.assign(options, {
|
return Object.assign(options, {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, scrub
|
from frappe import _, scrub
|
||||||
|
from frappe.query_builder import DocType
|
||||||
|
from frappe.query_builder.functions import IfNull
|
||||||
from frappe.utils import add_days, add_to_date, flt, getdate
|
from frappe.utils import add_days, add_to_date, flt, getdate
|
||||||
|
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
@@ -37,7 +39,26 @@ class Analytics:
|
|||||||
]
|
]
|
||||||
self.get_period_date_ranges()
|
self.get_period_date_ranges()
|
||||||
|
|
||||||
|
def update_company_list_for_parent_company(self):
|
||||||
|
company_list = [self.filters.get("company")]
|
||||||
|
|
||||||
|
selected_company = self.filters.get("company")
|
||||||
|
if (
|
||||||
|
selected_company
|
||||||
|
and self.filters.get("show_aggregate_value_from_subsidiary_companies")
|
||||||
|
and frappe.db.get_value("Company", selected_company, "is_group")
|
||||||
|
):
|
||||||
|
lft, rgt = frappe.db.get_value("Company", selected_company, ["lft", "rgt"])
|
||||||
|
child_companies = frappe.db.get_list(
|
||||||
|
"Company", filters={"lft": [">", lft], "rgt": ["<", rgt]}, pluck="name"
|
||||||
|
)
|
||||||
|
|
||||||
|
company_list.extend(child_companies)
|
||||||
|
|
||||||
|
self.filters["company"] = company_list
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
self.update_company_list_for_parent_company()
|
||||||
self.get_columns()
|
self.get_columns()
|
||||||
self.get_data()
|
self.get_data()
|
||||||
self.get_chart_data()
|
self.get_chart_data()
|
||||||
@@ -123,14 +144,23 @@ class Analytics:
|
|||||||
else:
|
else:
|
||||||
value_field = "total_qty"
|
value_field = "total_qty"
|
||||||
|
|
||||||
self.entries = frappe.db.sql(
|
doctype = DocType(self.filters.doc_type)
|
||||||
""" select s.order_type as entity, s.{value_field} as value_field, s.{date_field}
|
|
||||||
from `tab{doctype}` s where s.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s
|
self.entries = (
|
||||||
and ifnull(s.order_type, '') != '' order by s.order_type
|
frappe.qb.from_(doctype)
|
||||||
""".format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type),
|
.select(
|
||||||
(self.filters.company, self.filters.from_date, self.filters.to_date),
|
doctype.order_type.as_("entity"),
|
||||||
as_dict=1,
|
doctype[self.date_field],
|
||||||
)
|
doctype[value_field].as_("value_field"),
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(doctype.docstatus == 1)
|
||||||
|
& (doctype.company.isin(self.filters.company))
|
||||||
|
& (doctype[self.date_field].between(self.filters.from_date, self.filters.to_date))
|
||||||
|
& (IfNull(doctype.order_type, "") != "")
|
||||||
|
)
|
||||||
|
.orderby(doctype.order_type)
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
self.get_teams()
|
self.get_teams()
|
||||||
|
|
||||||
@@ -152,7 +182,7 @@ class Analytics:
|
|||||||
fields=[entity, entity_name, value_field, self.date_field],
|
fields=[entity, entity_name, value_field, self.date_field],
|
||||||
filters={
|
filters={
|
||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
"company": self.filters.company,
|
"company": ["in", self.filters.company],
|
||||||
self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
|
self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -167,16 +197,26 @@ class Analytics:
|
|||||||
else:
|
else:
|
||||||
value_field = "stock_qty"
|
value_field = "stock_qty"
|
||||||
|
|
||||||
self.entries = frappe.db.sql(
|
doctype = DocType(self.filters.doc_type)
|
||||||
"""
|
doctype_item = DocType(f"{self.filters.doc_type} Item")
|
||||||
select i.item_code as entity, i.item_name as entity_name, i.stock_uom, i.{value_field} as value_field, s.{date_field}
|
|
||||||
from `tab{doctype} Item` i , `tab{doctype}` s
|
self.entries = (
|
||||||
where s.name = i.parent and i.docstatus = 1 and s.company = %s
|
frappe.qb.from_(doctype_item)
|
||||||
and s.{date_field} between %s and %s
|
.join(doctype)
|
||||||
""".format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type),
|
.on(doctype.name == doctype_item.parent)
|
||||||
(self.filters.company, self.filters.from_date, self.filters.to_date),
|
.select(
|
||||||
as_dict=1,
|
doctype_item.item_code.as_("entity"),
|
||||||
)
|
doctype_item.item_name.as_("entity_name"),
|
||||||
|
doctype_item.stock_uom,
|
||||||
|
doctype_item[value_field].as_("value_field"),
|
||||||
|
doctype[self.date_field],
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(doctype_item.docstatus == 1)
|
||||||
|
& (doctype.company.isin(self.filters.company))
|
||||||
|
& (doctype[self.date_field].between(self.filters.from_date, self.filters.to_date))
|
||||||
|
)
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
self.entity_names = {}
|
self.entity_names = {}
|
||||||
for d in self.entries:
|
for d in self.entries:
|
||||||
@@ -201,7 +241,7 @@ class Analytics:
|
|||||||
fields=[entity_field, value_field, self.date_field],
|
fields=[entity_field, value_field, self.date_field],
|
||||||
filters={
|
filters={
|
||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
"company": self.filters.company,
|
"company": ["in", self.filters.company],
|
||||||
self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
|
self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -213,16 +253,24 @@ class Analytics:
|
|||||||
else:
|
else:
|
||||||
value_field = "qty"
|
value_field = "qty"
|
||||||
|
|
||||||
self.entries = frappe.db.sql(
|
doctype = DocType(self.filters.doc_type)
|
||||||
f"""
|
doctype_item = DocType(f"{self.filters.doc_type} Item")
|
||||||
select i.item_group as entity, i.{value_field} as value_field, s.{self.date_field}
|
|
||||||
from `tab{self.filters.doc_type} Item` i , `tab{self.filters.doc_type}` s
|
self.entries = (
|
||||||
where s.name = i.parent and i.docstatus = 1 and s.company = %s
|
frappe.qb.from_(doctype_item)
|
||||||
and s.{self.date_field} between %s and %s
|
.join(doctype)
|
||||||
""",
|
.on(doctype.name == doctype_item.parent)
|
||||||
(self.filters.company, self.filters.from_date, self.filters.to_date),
|
.select(
|
||||||
as_dict=1,
|
doctype_item.item_group.as_("entity"),
|
||||||
)
|
doctype_item[value_field].as_("value_field"),
|
||||||
|
doctype[self.date_field],
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(doctype_item.docstatus == 1)
|
||||||
|
& (doctype.company.isin(self.filters.company))
|
||||||
|
& (doctype[self.date_field].between(self.filters.from_date, self.filters.to_date))
|
||||||
|
)
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
self.get_groups()
|
self.get_groups()
|
||||||
|
|
||||||
@@ -239,7 +287,7 @@ class Analytics:
|
|||||||
fields=[entity, value_field, self.date_field],
|
fields=[entity, value_field, self.date_field],
|
||||||
filters={
|
filters={
|
||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
"company": self.filters.company,
|
"company": ["in", self.filters.company],
|
||||||
"project": ["!=", ""],
|
"project": ["!=", ""],
|
||||||
self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
|
self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
|
||||||
},
|
},
|
||||||
@@ -312,7 +360,7 @@ class Analytics:
|
|||||||
str(((posting_date.month - 1) // 3) + 1), str(posting_date.year)
|
str(((posting_date.month - 1) // 3) + 1), str(posting_date.year)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
year = get_fiscal_year(posting_date, company=self.filters.company)
|
year = get_fiscal_year(posting_date, company=self.filters.company[0])
|
||||||
period = str(year[0])
|
period = str(year[0])
|
||||||
return period
|
return period
|
||||||
|
|
||||||
|
|||||||
@@ -87,16 +87,19 @@
|
|||||||
"column_break_rxvc",
|
"column_break_rxvc",
|
||||||
"batch_no",
|
"batch_no",
|
||||||
"available_qty_section",
|
"available_qty_section",
|
||||||
"actual_batch_qty",
|
|
||||||
"actual_qty",
|
"actual_qty",
|
||||||
"installed_qty",
|
"actual_batch_qty",
|
||||||
"item_tax_rate",
|
|
||||||
"column_break_atna",
|
"column_break_atna",
|
||||||
|
"company_total_stock",
|
||||||
|
"section_break_kejd",
|
||||||
|
"installed_qty",
|
||||||
"packed_qty",
|
"packed_qty",
|
||||||
|
"column_break_fguf",
|
||||||
"received_qty",
|
"received_qty",
|
||||||
"accounting_details_section",
|
"accounting_details_section",
|
||||||
"expense_account",
|
"expense_account",
|
||||||
"column_break_71",
|
"column_break_71",
|
||||||
|
"item_tax_rate",
|
||||||
"internal_transfer_section",
|
"internal_transfer_section",
|
||||||
"material_request",
|
"material_request",
|
||||||
"purchase_order",
|
"purchase_order",
|
||||||
@@ -519,7 +522,7 @@
|
|||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "actual_qty",
|
"fieldname": "actual_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Available Qty at From Warehouse",
|
"label": "Qty (Warehouse)",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "actual_qty",
|
"oldfieldname": "actual_qty",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
@@ -907,13 +910,30 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_rxvc",
|
"fieldname": "column_break_rxvc",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "company_total_stock",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Qty (Company)",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_kejd",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_fguf",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-03-21 18:15:07.603672",
|
"modified": "2024-11-21 17:37:37.441498",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Delivery Note Item",
|
"name": "Delivery Note Item",
|
||||||
@@ -923,4 +943,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class DeliveryNoteItem(Document):
|
|||||||
batch_no: DF.Link | None
|
batch_no: DF.Link | None
|
||||||
billed_amt: DF.Currency
|
billed_amt: DF.Currency
|
||||||
brand: DF.Link | None
|
brand: DF.Link | None
|
||||||
|
company_total_stock: DF.Float
|
||||||
conversion_factor: DF.Float
|
conversion_factor: DF.Float
|
||||||
cost_center: DF.Link | None
|
cost_center: DF.Link | None
|
||||||
customer_item_code: DF.Data | None
|
customer_item_code: DF.Data | None
|
||||||
|
|||||||
@@ -663,39 +663,41 @@ $.extend(erpnext.item, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frm.doc.attributes.forEach(function (d) {
|
frm.doc.attributes.forEach(function (d) {
|
||||||
let p = new Promise((resolve) => {
|
if (!d.disabled) {
|
||||||
if (!d.numeric_values) {
|
let p = new Promise((resolve) => {
|
||||||
frappe
|
if (!d.numeric_values) {
|
||||||
.call({
|
frappe
|
||||||
method: "frappe.client.get_list",
|
.call({
|
||||||
args: {
|
method: "frappe.client.get_list",
|
||||||
doctype: "Item Attribute Value",
|
args: {
|
||||||
filters: [["parent", "=", d.attribute]],
|
doctype: "Item Attribute Value",
|
||||||
fields: ["attribute_value"],
|
filters: [["parent", "=", d.attribute]],
|
||||||
limit_page_length: 0,
|
fields: ["attribute_value"],
|
||||||
parent: "Item Attribute",
|
limit_page_length: 0,
|
||||||
order_by: "idx",
|
parent: "Item Attribute",
|
||||||
},
|
order_by: "idx",
|
||||||
})
|
},
|
||||||
.then((r) => {
|
})
|
||||||
if (r.message) {
|
.then((r) => {
|
||||||
attr_val_fields[d.attribute] = r.message.map(function (d) {
|
if (r.message) {
|
||||||
return d.attribute_value;
|
attr_val_fields[d.attribute] = r.message.map(function (d) {
|
||||||
});
|
return d.attribute_value;
|
||||||
resolve();
|
});
|
||||||
}
|
resolve();
|
||||||
});
|
}
|
||||||
} else {
|
});
|
||||||
let values = [];
|
} else {
|
||||||
for (var i = d.from_range; i <= d.to_range; i = flt(i + d.increment, 6)) {
|
let values = [];
|
||||||
values.push(i);
|
for (var i = d.from_range; i <= d.to_range; i = flt(i + d.increment, 6)) {
|
||||||
|
values.push(i);
|
||||||
|
}
|
||||||
|
attr_val_fields[d.attribute] = values;
|
||||||
|
resolve();
|
||||||
}
|
}
|
||||||
attr_val_fields[d.attribute] = values;
|
});
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
promises.push(p);
|
promises.push(p);
|
||||||
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
Promise.all(promises).then(() => {
|
Promise.all(promises).then(() => {
|
||||||
@@ -710,26 +712,29 @@ $.extend(erpnext.item, {
|
|||||||
for (var i = 0; i < frm.doc.attributes.length; i++) {
|
for (var i = 0; i < frm.doc.attributes.length; i++) {
|
||||||
var fieldtype, desc;
|
var fieldtype, desc;
|
||||||
var row = frm.doc.attributes[i];
|
var row = frm.doc.attributes[i];
|
||||||
if (row.numeric_values) {
|
|
||||||
fieldtype = "Float";
|
if (!row.disabled) {
|
||||||
desc =
|
if (row.numeric_values) {
|
||||||
"Min Value: " +
|
fieldtype = "Float";
|
||||||
row.from_range +
|
desc =
|
||||||
" , Max Value: " +
|
"Min Value: " +
|
||||||
row.to_range +
|
row.from_range +
|
||||||
", in Increments of: " +
|
" , Max Value: " +
|
||||||
row.increment;
|
row.to_range +
|
||||||
} else {
|
", in Increments of: " +
|
||||||
fieldtype = "Data";
|
row.increment;
|
||||||
desc = "";
|
} else {
|
||||||
|
fieldtype = "Data";
|
||||||
|
desc = "";
|
||||||
|
}
|
||||||
|
fields = fields.concat({
|
||||||
|
label: row.attribute,
|
||||||
|
fieldname: row.attribute,
|
||||||
|
fieldtype: fieldtype,
|
||||||
|
reqd: 0,
|
||||||
|
description: desc,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
fields = fields.concat({
|
|
||||||
label: row.attribute,
|
|
||||||
fieldname: row.attribute,
|
|
||||||
fieldtype: fieldtype,
|
|
||||||
reqd: 0,
|
|
||||||
description: desc,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.image) {
|
if (frm.doc.image) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"attribute_name",
|
"attribute_name",
|
||||||
"numeric_values",
|
"numeric_values",
|
||||||
|
"column_break_vbik",
|
||||||
|
"disabled",
|
||||||
"section_break_4",
|
"section_break_4",
|
||||||
"from_range",
|
"from_range",
|
||||||
"increment",
|
"increment",
|
||||||
@@ -70,15 +72,26 @@
|
|||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Item Attribute Values",
|
"label": "Item Attribute Values",
|
||||||
"options": "Item Attribute Value"
|
"options": "Item Attribute Value"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_vbik",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "disabled",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Disabled"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-edit",
|
"icon": "fa fa-edit",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-02 12:03:02.359202",
|
"modified": "2024-11-26 20:05:29.421714",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item Attribute",
|
"name": "Item Attribute",
|
||||||
|
"naming_rule": "By fieldname",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@@ -94,4 +107,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class ItemAttribute(Document):
|
|||||||
from erpnext.stock.doctype.item_attribute_value.item_attribute_value import ItemAttributeValue
|
from erpnext.stock.doctype.item_attribute_value.item_attribute_value import ItemAttributeValue
|
||||||
|
|
||||||
attribute_name: DF.Data
|
attribute_name: DF.Data
|
||||||
|
disabled: DF.Check
|
||||||
from_range: DF.Float
|
from_range: DF.Float
|
||||||
increment: DF.Float
|
increment: DF.Float
|
||||||
item_attribute_values: DF.Table[ItemAttributeValue]
|
item_attribute_values: DF.Table[ItemAttributeValue]
|
||||||
@@ -47,6 +48,19 @@ class ItemAttribute(Document):
|
|||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
self.validate_exising_items()
|
self.validate_exising_items()
|
||||||
|
self.set_enabled_disabled_in_items()
|
||||||
|
|
||||||
|
def set_enabled_disabled_in_items(self):
|
||||||
|
db_value = self.get_doc_before_save()
|
||||||
|
if not db_value or db_value.disabled != self.disabled:
|
||||||
|
item_variant_table = frappe.qb.DocType("Item Variant Attribute")
|
||||||
|
query = (
|
||||||
|
frappe.qb.update(item_variant_table)
|
||||||
|
.set(item_variant_table.disabled, self.disabled)
|
||||||
|
.where(item_variant_table.attribute == self.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
query.run()
|
||||||
|
|
||||||
def validate_exising_items(self):
|
def validate_exising_items(self):
|
||||||
"""Validate that if there are existing items with attributes, they are valid"""
|
"""Validate that if there are existing items with attributes, they are valid"""
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"column_break_2",
|
"column_break_2",
|
||||||
"attribute_value",
|
"attribute_value",
|
||||||
"numeric_values",
|
"numeric_values",
|
||||||
|
"disabled",
|
||||||
"section_break_4",
|
"section_break_4",
|
||||||
"from_range",
|
"from_range",
|
||||||
"increment",
|
"increment",
|
||||||
@@ -74,11 +75,18 @@
|
|||||||
"fieldname": "to_range",
|
"fieldname": "to_range",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "To Range"
|
"label": "To Range"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fetch_from": "attribute.disabled",
|
||||||
|
"fieldname": "disabled",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Disabled"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-07-14 17:15:19.112119",
|
"modified": "2024-11-26 20:10:49.873339",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item Variant Attribute",
|
"name": "Item Variant Attribute",
|
||||||
@@ -87,4 +95,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class ItemVariantAttribute(Document):
|
|||||||
|
|
||||||
attribute: DF.Link
|
attribute: DF.Link
|
||||||
attribute_value: DF.Data | None
|
attribute_value: DF.Data | None
|
||||||
|
disabled: DF.Check
|
||||||
from_range: DF.Float
|
from_range: DF.Float
|
||||||
increment: DF.Float
|
increment: DF.Float
|
||||||
numeric_values: DF.Check
|
numeric_values: DF.Check
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe.utils import cint, cstr, flt, get_number_format_info
|
from frappe.utils import cint, cstr, flt, get_link_to_form, get_number_format_info
|
||||||
|
|
||||||
from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template import (
|
from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template import (
|
||||||
get_template_details,
|
get_template_details,
|
||||||
@@ -73,6 +73,27 @@ class QualityInspection(Document):
|
|||||||
if self.readings:
|
if self.readings:
|
||||||
self.inspect_and_set_status()
|
self.inspect_and_set_status()
|
||||||
|
|
||||||
|
self.validate_inspection_required()
|
||||||
|
|
||||||
|
def validate_inspection_required(self):
|
||||||
|
if self.reference_type in ["Purchase Receipt", "Purchase Invoice"] and not frappe.get_cached_value(
|
||||||
|
"Item", self.item_code, "inspection_required_before_purchase"
|
||||||
|
):
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"'Inspection Required before Purchase' has disabled for the item {0}, no need to create the QI"
|
||||||
|
).format(get_link_to_form("Item", self.item_code))
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.reference_type in ["Delivery Note", "Sales Invoice"] and not frappe.get_cached_value(
|
||||||
|
"Item", self.item_code, "inspection_required_before_delivery"
|
||||||
|
):
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"'Inspection Required before Delivery' has disabled for the item {0}, no need to create the QI"
|
||||||
|
).format(get_link_to_form("Item", self.item_code))
|
||||||
|
)
|
||||||
|
|
||||||
def before_submit(self):
|
def before_submit(self):
|
||||||
self.validate_readings_status_mandatory()
|
self.validate_readings_status_mandatory()
|
||||||
|
|
||||||
|
|||||||
@@ -940,11 +940,12 @@ def get_batch_based_item_price(params, item_code) -> float:
|
|||||||
params = parse_json(params)
|
params = parse_json(params)
|
||||||
|
|
||||||
item_price = get_item_price(params, item_code, force_batch_no=True)
|
item_price = get_item_price(params, item_code, force_batch_no=True)
|
||||||
|
|
||||||
if not item_price:
|
if not item_price:
|
||||||
item_price = get_item_price(params, item_code, ignore_party=True, force_batch_no=True)
|
item_price = get_item_price(params, item_code, ignore_party=True, force_batch_no=True)
|
||||||
|
|
||||||
if item_price and item_price[0].uom == params.get("uom"):
|
if item_price and item_price[0][2] == params.get("uom"):
|
||||||
return item_price[0].price_list_rate
|
return item_price[0][1]
|
||||||
|
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
"document_type": "Warehouse",
|
"document_type": "Warehouse",
|
||||||
|
"dynamic_filters_json": "[[\"Warehouse\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
"filters_json": "[[\"Warehouse\",\"disabled\",\"=\",0]]",
|
"filters_json": "[[\"Warehouse\",\"disabled\",\"=\",0]]",
|
||||||
"function": "Count",
|
"function": "Count",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user