mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-25 04:53:01 +00:00
Merge branch 'develop' into fix-voucher-types
This commit is contained in:
@@ -148,14 +148,16 @@ class AccountsController(TransactionBase):
|
||||
def ensure_supplier_is_not_blocked(self):
|
||||
is_supplier_payment = self.doctype == "Payment Entry" and self.party_type == "Supplier"
|
||||
is_buying_invoice = self.doctype in ["Purchase Invoice", "Purchase Order"]
|
||||
supplier_name = self.supplier if is_buying_invoice else self.party if is_supplier_payment else None
|
||||
supplier = None
|
||||
supplier_name = None
|
||||
|
||||
if is_buying_invoice or is_supplier_payment:
|
||||
supplier_name = self.supplier if is_buying_invoice else self.party
|
||||
supplier = frappe.get_doc("Supplier", supplier_name)
|
||||
if supplier_name:
|
||||
supplier = frappe.get_doc(
|
||||
"Supplier",
|
||||
supplier_name,
|
||||
)
|
||||
|
||||
if supplier and supplier_name and supplier.on_hold:
|
||||
if supplier and supplier.on_hold:
|
||||
if (is_buying_invoice and supplier.hold_type in ["All", "Invoices"]) or (
|
||||
is_supplier_payment and supplier.hold_type in ["All", "Payments"]
|
||||
):
|
||||
@@ -345,9 +347,22 @@ class AccountsController(TransactionBase):
|
||||
repost_doc.flags.ignore_links = True
|
||||
repost_doc.save(ignore_permissions=True)
|
||||
|
||||
def _remove_advance_payment_ledger_entries(self):
|
||||
adv = qb.DocType("Advance Payment Ledger Entry")
|
||||
qb.from_(adv).delete().where(adv.voucher_type.eq(self.doctype) & adv.voucher_no.eq(self.name)).run()
|
||||
|
||||
advance_payment_doctypes = frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
|
||||
"advance_payment_payable_doctypes"
|
||||
)
|
||||
if self.doctype in advance_payment_doctypes:
|
||||
qb.from_(adv).delete().where(
|
||||
adv.against_voucher_type.eq(self.doctype) & adv.against_voucher_no.eq(self.name)
|
||||
).run()
|
||||
|
||||
def on_trash(self):
|
||||
from erpnext.accounts.utils import delete_exchange_gain_loss_journal
|
||||
|
||||
self._remove_advance_payment_ledger_entries()
|
||||
self._remove_references_in_repost_doctypes()
|
||||
self._remove_references_in_unreconcile()
|
||||
self.remove_serial_and_batch_bundle()
|
||||
@@ -393,12 +408,15 @@ class AccountsController(TransactionBase):
|
||||
def validate_return_against_account(self):
|
||||
if self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against:
|
||||
cr_dr_account_field = "debit_to" if self.doctype == "Sales Invoice" else "credit_to"
|
||||
cr_dr_account_label = "Debit To" if self.doctype == "Sales Invoice" else "Credit To"
|
||||
cr_dr_account = self.get(cr_dr_account_field)
|
||||
if frappe.get_value(self.doctype, self.return_against, cr_dr_account_field) != cr_dr_account:
|
||||
original_account = frappe.get_value(self.doctype, self.return_against, cr_dr_account_field)
|
||||
if original_account != self.get(cr_dr_account_field):
|
||||
frappe.throw(
|
||||
_("'{0}' account: '{1}' should match the Return Against Invoice").format(
|
||||
frappe.bold(cr_dr_account_label), frappe.bold(cr_dr_account)
|
||||
_(
|
||||
"Please set {0} to {1}, the same account that was used in the original invoice {2}."
|
||||
).format(
|
||||
frappe.bold(_(self.meta.get_label(cr_dr_account_field), context=self.doctype)),
|
||||
frappe.bold(original_account),
|
||||
frappe.bold(self.return_against),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -448,6 +466,11 @@ class AccountsController(TransactionBase):
|
||||
)
|
||||
|
||||
def validate_invoice_documents_schedule(self):
|
||||
if self.is_return:
|
||||
self.payment_terms_template = ""
|
||||
self.payment_schedule = []
|
||||
return
|
||||
|
||||
self.validate_payment_schedule_dates()
|
||||
self.set_due_date()
|
||||
self.set_payment_schedule()
|
||||
@@ -462,7 +485,7 @@ class AccountsController(TransactionBase):
|
||||
self.validate_payment_schedule_amount()
|
||||
|
||||
def validate_all_documents_schedule(self):
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
self.validate_invoice_documents_schedule()
|
||||
elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"):
|
||||
self.validate_non_invoice_documents_schedule()
|
||||
@@ -1039,7 +1062,9 @@ class AccountsController(TransactionBase):
|
||||
gl_dict.update(
|
||||
{
|
||||
"transaction_currency": self.get("currency") or self.company_currency,
|
||||
"transaction_exchange_rate": self.get("conversion_rate", 1),
|
||||
"transaction_exchange_rate": item.get("exchange_rate", 1)
|
||||
if self.doctype == "Journal Entry" and item
|
||||
else self.get("conversion_rate", 1),
|
||||
"debit_in_transaction_currency": self.get_value_in_transaction_currency(
|
||||
account_currency, gl_dict, "debit"
|
||||
),
|
||||
@@ -1064,6 +1089,13 @@ class AccountsController(TransactionBase):
|
||||
"Stock Entry": "stock_entry_type",
|
||||
"Asset Capitalization": "entry_type",
|
||||
}
|
||||
|
||||
for method_name in frappe.get_hooks("voucher_subtypes"):
|
||||
voucher_subtype = frappe.get_attr(method_name)(self)
|
||||
|
||||
if voucher_subtype:
|
||||
return voucher_subtype
|
||||
|
||||
if self.doctype in voucher_subtypes:
|
||||
return self.get(voucher_subtypes[self.doctype])
|
||||
elif self.doctype == "Purchase Receipt" and self.is_return:
|
||||
@@ -1076,6 +1108,7 @@ class AccountsController(TransactionBase):
|
||||
return "Debit Note"
|
||||
elif self.doctype == "Purchase Invoice" and self.is_return:
|
||||
return "Debit Note"
|
||||
|
||||
return self.doctype
|
||||
|
||||
def get_value_in_transaction_currency(self, account_currency, gl_dict, field):
|
||||
@@ -1909,25 +1942,22 @@ class AccountsController(TransactionBase):
|
||||
|
||||
return stock_items
|
||||
|
||||
def set_total_advance_paid(self):
|
||||
ple = frappe.qb.DocType("Payment Ledger Entry")
|
||||
if self.doctype in frappe.get_hooks("advance_payment_receivable_doctypes"):
|
||||
party = self.customer
|
||||
if self.doctype in frappe.get_hooks("advance_payment_payable_doctypes"):
|
||||
party = self.supplier
|
||||
def calculate_total_advance_from_ledger(self):
|
||||
adv = frappe.qb.DocType("Advance Payment Ledger Entry")
|
||||
advance = (
|
||||
frappe.qb.from_(ple)
|
||||
.select(ple.account_currency, Abs(Sum(ple.amount_in_account_currency)).as_("amount"))
|
||||
frappe.qb.from_(adv)
|
||||
.select(adv.currency.as_("account_currency"), Abs(Sum(adv.amount)).as_("amount"))
|
||||
.where(
|
||||
(ple.against_voucher_type == self.doctype)
|
||||
& (ple.against_voucher_no == self.name)
|
||||
& (ple.party == party)
|
||||
& (ple.delinked == 0)
|
||||
& (ple.company == self.company)
|
||||
(adv.against_voucher_type == self.doctype)
|
||||
& (adv.against_voucher_no == self.name)
|
||||
& (adv.company == self.company)
|
||||
)
|
||||
.run(as_dict=True)
|
||||
)
|
||||
return advance
|
||||
|
||||
def set_total_advance_paid(self):
|
||||
advance = self.calculate_total_advance_from_ledger()
|
||||
advance_paid, order_total = None, None
|
||||
|
||||
if advance:
|
||||
@@ -1968,33 +1998,24 @@ class AccountsController(TransactionBase):
|
||||
def set_advance_payment_status(self):
|
||||
new_status = None
|
||||
|
||||
stati = frappe.get_all(
|
||||
"Payment Request",
|
||||
{
|
||||
paid_amount = frappe.get_value(
|
||||
doctype="Payment Request",
|
||||
filters={
|
||||
"reference_doctype": self.doctype,
|
||||
"reference_name": self.name,
|
||||
"docstatus": 1,
|
||||
},
|
||||
pluck="status",
|
||||
fieldname="sum(grand_total - outstanding_amount)",
|
||||
)
|
||||
if self.doctype in frappe.get_hooks("advance_payment_receivable_doctypes"):
|
||||
if not stati:
|
||||
new_status = "Not Requested"
|
||||
elif "Requested" in stati or "Failed" in stati:
|
||||
new_status = "Requested"
|
||||
elif "Partially Paid" in stati:
|
||||
new_status = "Partially Paid"
|
||||
elif "Paid" in stati:
|
||||
new_status = "Fully Paid"
|
||||
if self.doctype in frappe.get_hooks("advance_payment_payable_doctypes"):
|
||||
if not stati:
|
||||
new_status = "Not Initiated"
|
||||
elif "Initiated" in stati or "Failed" in stati or "Payment Ordered" in stati:
|
||||
new_status = "Initiated"
|
||||
elif "Partially Paid" in stati:
|
||||
new_status = "Partially Paid"
|
||||
elif "Paid" in stati:
|
||||
new_status = "Fully Paid"
|
||||
|
||||
if not paid_amount:
|
||||
if self.doctype in frappe.get_hooks("advance_payment_receivable_doctypes"):
|
||||
new_status = "Not Requested" if paid_amount is None else "Requested"
|
||||
elif self.doctype in frappe.get_hooks("advance_payment_payable_doctypes"):
|
||||
new_status = "Not Initiated" if paid_amount is None else "Initiated"
|
||||
else:
|
||||
total_amount = self.get("rounded_total") or self.get("grand_total")
|
||||
new_status = "Fully Paid" if paid_amount == total_amount else "Partially Paid"
|
||||
|
||||
if new_status == self.advance_payment_status:
|
||||
return
|
||||
@@ -2179,8 +2200,8 @@ class AccountsController(TransactionBase):
|
||||
date = self.get("due_date")
|
||||
due_date = date or posting_date
|
||||
|
||||
base_grand_total = self.get("base_rounded_total") or self.base_grand_total
|
||||
grand_total = self.get("rounded_total") or self.grand_total
|
||||
base_grand_total = flt(self.get("base_rounded_total") or self.base_grand_total)
|
||||
grand_total = flt(self.get("rounded_total") or self.grand_total)
|
||||
automatically_fetch_payment_terms = 0
|
||||
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
@@ -2254,6 +2275,8 @@ class AccountsController(TransactionBase):
|
||||
self.ignore_default_payment_terms_template = 1
|
||||
|
||||
def get_order_details(self):
|
||||
if not self.get("items"):
|
||||
return None, None, None
|
||||
if self.doctype == "Sales Invoice":
|
||||
po_or_so = self.get("items")[0].get("sales_order")
|
||||
po_or_so_doctype = "Sales Order"
|
||||
@@ -2364,8 +2387,8 @@ class AccountsController(TransactionBase):
|
||||
total += flt(d.payment_amount, d.precision("payment_amount"))
|
||||
base_total += flt(d.base_payment_amount, d.precision("base_payment_amount"))
|
||||
|
||||
base_grand_total = self.get("base_rounded_total") or self.base_grand_total
|
||||
grand_total = self.get("rounded_total") or self.grand_total
|
||||
base_grand_total = flt(self.get("base_rounded_total") or self.base_grand_total)
|
||||
grand_total = flt(self.get("rounded_total") or self.grand_total)
|
||||
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
base_grand_total = base_grand_total - flt(self.base_write_off_amount)
|
||||
@@ -2565,6 +2588,69 @@ class AccountsController(TransactionBase):
|
||||
repost_ledger.insert()
|
||||
repost_ledger.submit()
|
||||
|
||||
def get_advance_payment_doctypes(self) -> list:
|
||||
return frappe.get_hooks("advance_payment_receivable_doctypes") + frappe.get_hooks(
|
||||
"advance_payment_payable_doctypes"
|
||||
)
|
||||
|
||||
def make_advance_payment_ledger_for_journal(self):
|
||||
advance_payment_doctypes = self.get_advance_payment_doctypes()
|
||||
advance_doctype_references = [
|
||||
x for x in self.accounts if x.reference_type in advance_payment_doctypes
|
||||
]
|
||||
|
||||
for x in advance_doctype_references:
|
||||
# Looking for payments
|
||||
dr_or_cr = (
|
||||
"credit_in_account_currency"
|
||||
if x.account_type == "Receivable"
|
||||
else "debit_in_account_currency"
|
||||
)
|
||||
|
||||
amount = x.get(dr_or_cr)
|
||||
if amount > 0:
|
||||
doc = frappe.new_doc("Advance Payment Ledger Entry")
|
||||
doc.company = self.company
|
||||
doc.voucher_type = self.doctype
|
||||
doc.voucher_no = self.name
|
||||
doc.against_voucher_type = x.reference_type
|
||||
doc.against_voucher_no = x.reference_name
|
||||
doc.amount = amount if self.docstatus == 1 else -1 * amount
|
||||
doc.event = "Submit" if self.docstatus == 1 else "Cancel"
|
||||
doc.currency = x.account_currency
|
||||
doc.flags.ignore_permissions = 1
|
||||
doc.save()
|
||||
|
||||
def make_advance_payment_ledger_for_payment(self):
|
||||
advance_payment_doctypes = self.get_advance_payment_doctypes()
|
||||
advance_doctype_references = [
|
||||
x for x in self.references if x.reference_doctype in advance_payment_doctypes
|
||||
]
|
||||
currency = (
|
||||
self.paid_from_account_currency
|
||||
if self.payment_type == "Receive"
|
||||
else self.paid_to_account_currency
|
||||
)
|
||||
for x in advance_doctype_references:
|
||||
doc = frappe.new_doc("Advance Payment Ledger Entry")
|
||||
doc.company = self.company
|
||||
doc.voucher_type = self.doctype
|
||||
doc.voucher_no = self.name
|
||||
doc.against_voucher_type = x.reference_doctype
|
||||
doc.against_voucher_no = x.reference_name
|
||||
doc.amount = x.allocated_amount if self.docstatus == 1 else -1 * x.allocated_amount
|
||||
doc.currency = currency
|
||||
doc.event = "Submit" if self.docstatus == 1 else "Cancel"
|
||||
doc.flags.ignore_permissions = 1
|
||||
doc.save()
|
||||
|
||||
def make_advance_payment_ledger_entries(self):
|
||||
if self.docstatus != 0:
|
||||
if self.doctype == "Journal Entry":
|
||||
self.make_advance_payment_ledger_for_journal()
|
||||
elif self.doctype == "Payment Entry":
|
||||
self.make_advance_payment_ledger_for_payment()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_tax_rate(account_head):
|
||||
@@ -2648,21 +2734,21 @@ def validate_taxes_and_charges(tax):
|
||||
tax.rate = None
|
||||
|
||||
|
||||
def validate_account_head(idx, account, company, context=""):
|
||||
account_company = frappe.get_cached_value("Account", account, "company")
|
||||
is_group = frappe.get_cached_value("Account", account, "is_group")
|
||||
|
||||
if account_company != company:
|
||||
def validate_account_head(idx: int, account: str, company: str, context: str | None = None) -> None:
|
||||
"""Throw a ValidationError if the account belongs to a different company or is a group account."""
|
||||
if company != frappe.get_cached_value("Account", account, "company"):
|
||||
frappe.throw(
|
||||
_("Row {0}: {3} Account {1} does not belong to Company {2}").format(
|
||||
idx, frappe.bold(account), frappe.bold(company), context
|
||||
_("Row {0}: The {3} Account {1} does not belong to the company {2}").format(
|
||||
idx, frappe.bold(account), frappe.bold(company), context or ""
|
||||
),
|
||||
title=_("Invalid Account"),
|
||||
)
|
||||
|
||||
if is_group:
|
||||
if frappe.get_cached_value("Account", account, "is_group"):
|
||||
frappe.throw(
|
||||
_("Row {0}: Account {1} is a Group Account").format(idx, frappe.bold(account)),
|
||||
_(
|
||||
"You selected the account group {1} as {2} Account in row {0}. Please select a single account."
|
||||
).format(idx, frappe.bold(account), context or ""),
|
||||
title=_("Invalid Account"),
|
||||
)
|
||||
|
||||
@@ -3344,7 +3430,6 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
items_added_or_removed = False # updated to true if any new item is added or removed
|
||||
any_conversion_factor_changed = False
|
||||
|
||||
sales_doctypes = ["Sales Order", "Sales Invoice", "Delivery Note", "Quotation"]
|
||||
parent = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
|
||||
check_doc_permissions(parent, "write")
|
||||
@@ -3460,25 +3545,21 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
# if rate is greater than price_list_rate, set margin
|
||||
# or set discount
|
||||
child_item.discount_percentage = 0
|
||||
|
||||
if parent_doctype in sales_doctypes:
|
||||
child_item.margin_type = "Amount"
|
||||
child_item.margin_rate_or_amount = flt(
|
||||
child_item.rate - child_item.price_list_rate,
|
||||
child_item.precision("margin_rate_or_amount"),
|
||||
)
|
||||
child_item.rate_with_margin = child_item.rate
|
||||
child_item.margin_type = "Amount"
|
||||
child_item.margin_rate_or_amount = flt(
|
||||
child_item.rate - child_item.price_list_rate,
|
||||
child_item.precision("margin_rate_or_amount"),
|
||||
)
|
||||
child_item.rate_with_margin = child_item.rate
|
||||
else:
|
||||
child_item.discount_percentage = flt(
|
||||
(1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0,
|
||||
child_item.precision("discount_percentage"),
|
||||
)
|
||||
child_item.discount_amount = flt(child_item.price_list_rate) - flt(child_item.rate)
|
||||
|
||||
if parent_doctype in sales_doctypes:
|
||||
child_item.margin_type = ""
|
||||
child_item.margin_rate_or_amount = 0
|
||||
child_item.rate_with_margin = 0
|
||||
child_item.margin_type = ""
|
||||
child_item.margin_rate_or_amount = 0
|
||||
child_item.rate_with_margin = 0
|
||||
|
||||
child_item.flags.ignore_validate_update_after_submit = True
|
||||
if new_child_flag:
|
||||
@@ -3553,6 +3634,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
parent.update_billing_percentage()
|
||||
parent.set_status()
|
||||
|
||||
parent.validate_uom_is_integer("uom", "qty")
|
||||
parent.validate_uom_is_integer("stock_uom", "stock_qty")
|
||||
|
||||
# Cancel and Recreate Stock Reservation Entries.
|
||||
if parent_doctype == "Sales Order":
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
|
||||
@@ -251,16 +251,16 @@ class BuyingController(SubcontractingController):
|
||||
|
||||
if self.meta.get_field("base_in_words"):
|
||||
if self.meta.get_field("base_rounded_total") and not self.is_rounded_total_disabled():
|
||||
amount = abs(self.base_rounded_total)
|
||||
amount = abs(flt(self.base_rounded_total))
|
||||
else:
|
||||
amount = abs(self.base_grand_total)
|
||||
amount = abs(flt(self.base_grand_total))
|
||||
self.base_in_words = money_in_words(amount, self.company_currency)
|
||||
|
||||
if self.meta.get_field("in_words"):
|
||||
if self.meta.get_field("rounded_total") and not self.is_rounded_total_disabled():
|
||||
amount = abs(self.rounded_total)
|
||||
amount = abs(flt(self.rounded_total))
|
||||
else:
|
||||
amount = abs(self.grand_total)
|
||||
amount = abs(flt(self.grand_total))
|
||||
|
||||
self.in_words = money_in_words(amount, self.currency)
|
||||
|
||||
@@ -702,9 +702,11 @@ class BuyingController(SubcontractingController):
|
||||
if self.get("is_return"):
|
||||
return
|
||||
|
||||
if self.doctype in ["Purchase Order", "Purchase Receipt"] and not frappe.db.get_single_value(
|
||||
"Buying Settings", "disable_last_purchase_rate"
|
||||
):
|
||||
if self.doctype in [
|
||||
"Purchase Order",
|
||||
"Purchase Receipt",
|
||||
"Purchase Invoice",
|
||||
] and not frappe.db.get_single_value("Buying Settings", "disable_last_purchase_rate"):
|
||||
update_last_purchase_rate(self, is_submit=0)
|
||||
|
||||
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||
|
||||
@@ -79,6 +79,9 @@ def validate_returned_items(doc):
|
||||
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
|
||||
select_fields += ",rejected_qty, received_qty"
|
||||
|
||||
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||
select_fields += ",name"
|
||||
|
||||
for d in frappe.db.sql(
|
||||
f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""",
|
||||
doc.return_against,
|
||||
@@ -104,15 +107,24 @@ def validate_returned_items(doc):
|
||||
|
||||
items_returned = False
|
||||
for d in doc.get("items"):
|
||||
key = d.item_code
|
||||
raise_exception = False
|
||||
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||
field = frappe.scrub(doc.doctype) + "_item"
|
||||
if d.get(field):
|
||||
key = (d.item_code, d.get(field))
|
||||
raise_exception = True
|
||||
|
||||
if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
|
||||
if d.item_code not in valid_items:
|
||||
frappe.throw(
|
||||
if key not in valid_items:
|
||||
frappe.msgprint(
|
||||
_("Row # {0}: Returned Item {1} does not exist in {2} {3}").format(
|
||||
d.idx, d.item_code, doc.doctype, doc.return_against
|
||||
)
|
||||
),
|
||||
raise_exception=raise_exception,
|
||||
)
|
||||
else:
|
||||
ref = valid_items.get(d.item_code, frappe._dict())
|
||||
ref = valid_items.get(key, frappe._dict())
|
||||
validate_quantity(doc, d, ref, valid_items, already_returned_items)
|
||||
|
||||
if (
|
||||
@@ -193,8 +205,12 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
|
||||
def get_ref_item_dict(valid_items, ref_item_row):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
key = ref_item_row.item_code
|
||||
if ref_item_row.get("name"):
|
||||
key = (ref_item_row.item_code, ref_item_row.name)
|
||||
|
||||
valid_items.setdefault(
|
||||
ref_item_row.item_code,
|
||||
key,
|
||||
frappe._dict(
|
||||
{
|
||||
"qty": 0,
|
||||
@@ -208,7 +224,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
|
||||
}
|
||||
),
|
||||
)
|
||||
item_dict = valid_items[ref_item_row.item_code]
|
||||
item_dict = valid_items[key]
|
||||
item_dict["qty"] += ref_item_row.qty
|
||||
item_dict["stock_qty"] += ref_item_row.get("stock_qty", 0)
|
||||
if ref_item_row.get("rate", 0) > item_dict["rate"]:
|
||||
@@ -335,6 +351,9 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
|
||||
doc.select_print_heading = frappe.get_cached_value("Print Heading", _("Debit Note"))
|
||||
if source.tax_withholding_category:
|
||||
doc.set_onload("supplier_tds", source.tax_withholding_category)
|
||||
elif doctype == "Delivery Note":
|
||||
# manual additions to the return should hit the return warehous, too
|
||||
doc.set_warehouse = default_warehouse_for_sales_return
|
||||
|
||||
for tax in doc.get("taxes") or []:
|
||||
if tax.charge_type == "Actual":
|
||||
@@ -581,6 +600,10 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
|
||||
if default_warehouse_for_sales_return:
|
||||
target_doc.warehouse = default_warehouse_for_sales_return
|
||||
|
||||
if not source_doc.use_serial_batch_fields and source_doc.serial_and_batch_bundle:
|
||||
target_doc.serial_no = None
|
||||
target_doc.batch_no = None
|
||||
|
||||
if (
|
||||
(source_doc.serial_no or source_doc.batch_no)
|
||||
and not source_doc.serial_and_batch_bundle
|
||||
@@ -883,6 +906,7 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
||||
"`tabSerial and Batch Entry`.`serial_no`",
|
||||
"`tabSerial and Batch Entry`.`batch_no`",
|
||||
"`tabSerial and Batch Entry`.`qty`",
|
||||
"`tabSerial and Batch Entry`.`incoming_rate`",
|
||||
"`tabSerial and Batch Bundle`.`voucher_detail_no`",
|
||||
"`tabSerial and Batch Bundle`.`voucher_type`",
|
||||
"`tabSerial and Batch Bundle`.`voucher_no`",
|
||||
@@ -904,15 +928,23 @@ def get_serial_batches_based_on_bundle(field, _bundle_ids):
|
||||
|
||||
if key not in available_dict:
|
||||
available_dict[key] = frappe._dict(
|
||||
{"qty": 0.0, "serial_nos": defaultdict(float), "batches": defaultdict(float)}
|
||||
{
|
||||
"qty": 0.0,
|
||||
"serial_nos": defaultdict(float),
|
||||
"batches": defaultdict(float),
|
||||
"serial_nos_valuation": defaultdict(float),
|
||||
"batches_valuation": defaultdict(float),
|
||||
}
|
||||
)
|
||||
|
||||
available_dict[key]["qty"] += row.qty
|
||||
|
||||
if row.serial_no:
|
||||
available_dict[key]["serial_nos"][row.serial_no] += row.qty
|
||||
available_dict[key]["serial_nos_valuation"][row.serial_no] = row.incoming_rate
|
||||
elif row.batch_no:
|
||||
available_dict[key]["batches"][row.batch_no] += row.qty
|
||||
available_dict[key]["batches_valuation"][row.batch_no] = row.incoming_rate
|
||||
|
||||
return available_dict
|
||||
|
||||
@@ -948,12 +980,13 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False
|
||||
)
|
||||
)
|
||||
else:
|
||||
fields = [
|
||||
"serial_and_batch_bundle",
|
||||
]
|
||||
fields = ["serial_and_batch_bundle"]
|
||||
|
||||
if is_rejected:
|
||||
fields.extend(["rejected_serial_and_batch_bundle", "return_qty_from_rejected_warehouse"])
|
||||
fields.append("rejected_serial_and_batch_bundle")
|
||||
|
||||
if doctype == "Purchase Receipt Item":
|
||||
fields.append("return_qty_from_rejected_warehouse")
|
||||
|
||||
del filters["rejected_serial_and_batch_bundle"]
|
||||
data = frappe.get_all(
|
||||
@@ -987,7 +1020,14 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field
|
||||
warehouse = row.get(warehouse_field)
|
||||
qty = abs(row.get(qty_field))
|
||||
|
||||
filterd_serial_batch = frappe._dict({"serial_nos": [], "batches": defaultdict(float)})
|
||||
filterd_serial_batch = frappe._dict(
|
||||
{
|
||||
"serial_nos": [],
|
||||
"batches": defaultdict(float),
|
||||
"serial_nos_valuation": data.get("serial_nos_valuation"),
|
||||
"batches_valuation": data.get("batches_valuation"),
|
||||
}
|
||||
)
|
||||
|
||||
if data.serial_nos:
|
||||
available_serial_nos = []
|
||||
@@ -997,7 +1037,7 @@ def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field
|
||||
|
||||
if available_serial_nos:
|
||||
if parent_doc.doctype in ["Purchase Invoice", "Purchase Reecipt"]:
|
||||
available_serial_nos = get_available_serial_nos(available_serial_nos)
|
||||
available_serial_nos = get_available_serial_nos(available_serial_nos, warehouse)
|
||||
|
||||
if len(available_serial_nos) > qty:
|
||||
filterd_serial_batch["serial_nos"] = sorted(available_serial_nos[0 : cint(qty)])
|
||||
@@ -1082,6 +1122,8 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f
|
||||
"warehouse": warehouse,
|
||||
"serial_nos": data.get("serial_nos"),
|
||||
"batches": data.get("batches"),
|
||||
"serial_nos_valuation": data.get("serial_nos_valuation"),
|
||||
"batches_valuation": data.get("batches_valuation"),
|
||||
"posting_date": parent_doc.posting_date,
|
||||
"posting_time": parent_doc.posting_time,
|
||||
"voucher_type": parent_doc.doctype,
|
||||
|
||||
@@ -470,6 +470,16 @@ class SellingController(StockController):
|
||||
raise_error_if_no_rate=False,
|
||||
)
|
||||
|
||||
if (
|
||||
not d.incoming_rate
|
||||
and self.get("return_against")
|
||||
and self.get("is_return")
|
||||
and get_valuation_method(d.item_code) == "Moving Average"
|
||||
):
|
||||
d.incoming_rate = get_rate_for_return(
|
||||
self.doctype, self.name, d.item_code, self.return_against, item_row=d
|
||||
)
|
||||
|
||||
# For internal transfers use incoming rate as the valuation rate
|
||||
if self.is_internal_transfer():
|
||||
if self.doctype == "Delivery Note" or self.get("update_stock"):
|
||||
|
||||
@@ -63,6 +63,33 @@ class StockController(AccountsController):
|
||||
self.set_rate_of_stock_uom()
|
||||
self.validate_internal_transfer()
|
||||
self.validate_putaway_capacity()
|
||||
self.reset_conversion_factor()
|
||||
|
||||
def reset_conversion_factor(self):
|
||||
for row in self.get("items"):
|
||||
if row.uom != row.stock_uom:
|
||||
continue
|
||||
|
||||
if row.conversion_factor != 1.0:
|
||||
row.conversion_factor = 1.0
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"Conversion factor for item {0} has been reset to 1.0 as the uom {1} is same as stock uom {2}."
|
||||
).format(bold(row.item_code), bold(row.uom), bold(row.stock_uom)),
|
||||
alert=True,
|
||||
)
|
||||
|
||||
def validate_items_exist(self):
|
||||
if not self.get("items"):
|
||||
return
|
||||
|
||||
items = [d.item_code for d in self.get("items")]
|
||||
|
||||
exists_items = frappe.get_all("Item", filters={"name": ("in", items)}, pluck="name")
|
||||
non_exists_items = set(items) - set(exists_items)
|
||||
|
||||
if non_exists_items:
|
||||
frappe.throw(_("Items {0} do not exist in the Item master.").format(", ".join(non_exists_items)))
|
||||
|
||||
def validate_duplicate_serial_and_batch_bundle(self, table_name):
|
||||
if not self.get(table_name):
|
||||
@@ -307,6 +334,11 @@ class StockController(AccountsController):
|
||||
}
|
||||
)
|
||||
|
||||
if self.doctype in ["Sales Invoice", "Delivery Note"]:
|
||||
row.db_set(
|
||||
"incoming_rate", frappe.db.get_value("Serial and Batch Bundle", bundle, "avg_rate")
|
||||
)
|
||||
|
||||
def get_reference_ids(self, table_name, qty_field=None, bundle_field=None) -> tuple[str, list[str]]:
|
||||
field = {
|
||||
"Sales Invoice": "sales_invoice_item",
|
||||
@@ -345,6 +377,9 @@ class StockController(AccountsController):
|
||||
|
||||
@frappe.request_cache
|
||||
def is_serial_batch_item(self, item_code) -> bool:
|
||||
if not frappe.db.exists("Item", item_code):
|
||||
frappe.throw(_("Item {0} does not exist.").format(bold(item_code)))
|
||||
|
||||
item_details = frappe.db.get_value("Item", item_code, ["has_serial_no", "has_batch_no"], as_dict=1)
|
||||
|
||||
if item_details.has_serial_no or item_details.has_batch_no:
|
||||
|
||||
@@ -561,11 +561,11 @@ class SubcontractingController(StockController):
|
||||
use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields")
|
||||
|
||||
if self.doctype == self.subcontract_data.order_doctype:
|
||||
rm_obj.required_qty = qty
|
||||
rm_obj.amount = rm_obj.required_qty * rm_obj.rate
|
||||
rm_obj.required_qty = flt(qty, rm_obj.precision("required_qty"))
|
||||
rm_obj.amount = flt(rm_obj.required_qty * rm_obj.rate, rm_obj.precision("amount"))
|
||||
else:
|
||||
rm_obj.consumed_qty = qty
|
||||
rm_obj.required_qty = bom_item.required_qty or qty
|
||||
rm_obj.consumed_qty = flt(qty, rm_obj.precision("consumed_qty"))
|
||||
rm_obj.required_qty = flt(bom_item.required_qty or qty, rm_obj.precision("required_qty"))
|
||||
rm_obj.serial_and_batch_bundle = None
|
||||
setattr(
|
||||
rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field)
|
||||
@@ -576,30 +576,56 @@ class SubcontractingController(StockController):
|
||||
self.__set_batch_nos(bom_item, item_row, rm_obj, qty)
|
||||
|
||||
if self.doctype == "Subcontracting Receipt" and not use_serial_batch_fields:
|
||||
args = frappe._dict(
|
||||
{
|
||||
"item_code": rm_obj.rm_item_code,
|
||||
"warehouse": self.supplier_warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": -1 * flt(rm_obj.consumed_qty),
|
||||
"actual_qty": -1 * flt(rm_obj.consumed_qty),
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"voucher_detail_no": item_row.name,
|
||||
"company": self.company,
|
||||
"allow_zero_valuation": 1,
|
||||
}
|
||||
)
|
||||
|
||||
rm_obj.serial_and_batch_bundle = self.__set_serial_and_batch_bundle(
|
||||
item_row, rm_obj, rm_obj.consumed_qty
|
||||
)
|
||||
|
||||
if rm_obj.serial_and_batch_bundle:
|
||||
args["serial_and_batch_bundle"] = rm_obj.serial_and_batch_bundle
|
||||
self.set_rate_for_supplied_items(rm_obj, item_row)
|
||||
|
||||
rm_obj.rate = get_incoming_rate(args)
|
||||
def update_rate_for_supplied_items(self):
|
||||
if self.doctype != "Subcontracting Receipt":
|
||||
return
|
||||
|
||||
for row in self.supplied_items:
|
||||
item_row = None
|
||||
if row.reference_name:
|
||||
item_row = self.get_item_row(row.reference_name)
|
||||
|
||||
if not item_row:
|
||||
continue
|
||||
|
||||
self.set_rate_for_supplied_items(row, item_row)
|
||||
|
||||
def get_item_row(self, reference_name):
|
||||
for item in self.items:
|
||||
if item.name == reference_name:
|
||||
return item
|
||||
|
||||
def set_rate_for_supplied_items(self, rm_obj, item_row):
|
||||
args = frappe._dict(
|
||||
{
|
||||
"item_code": rm_obj.rm_item_code,
|
||||
"warehouse": self.supplier_warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": -1 * flt(rm_obj.consumed_qty),
|
||||
"actual_qty": -1 * flt(rm_obj.consumed_qty),
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"voucher_detail_no": item_row.name,
|
||||
"company": self.company,
|
||||
"allow_zero_valuation": 1,
|
||||
}
|
||||
)
|
||||
|
||||
if rm_obj.serial_and_batch_bundle:
|
||||
args["serial_and_batch_bundle"] = rm_obj.serial_and_batch_bundle
|
||||
|
||||
if rm_obj.use_serial_batch_fields:
|
||||
args["batch_no"] = rm_obj.batch_no
|
||||
args["serial_no"] = rm_obj.serial_no
|
||||
|
||||
rm_obj.rate = get_incoming_rate(args)
|
||||
|
||||
def __set_batch_nos(self, bom_item, item_row, rm_obj, qty):
|
||||
key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
|
||||
@@ -638,8 +664,8 @@ class SubcontractingController(StockController):
|
||||
self.__set_serial_nos(item_row, rm_obj)
|
||||
|
||||
def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0):
|
||||
rm_obj.required_qty = required_qty
|
||||
rm_obj.consumed_qty = consumed_qty
|
||||
rm_obj.required_qty = flt(required_qty, rm_obj.precision("required_qty"))
|
||||
rm_obj.consumed_qty = flt(consumed_qty, rm_obj.precision("consumed_qty"))
|
||||
|
||||
def __set_serial_nos(self, item_row, rm_obj):
|
||||
key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field))
|
||||
@@ -1209,6 +1235,17 @@ def add_items_in_ste(ste_doc, row, qty, rm_details, rm_detail_field="sco_rm_deta
|
||||
def make_return_stock_entry_for_subcontract(
|
||||
available_materials, order_doc, rm_details, order_doctype="Subcontracting Order"
|
||||
):
|
||||
def post_process(source_doc, target_doc):
|
||||
target_doc.purpose = "Material Transfer"
|
||||
|
||||
if source_doc.doctype == "Purchase Order":
|
||||
target_doc.purchase_order = source_doc.name
|
||||
else:
|
||||
target_doc.subcontracting_order = source_doc.name
|
||||
|
||||
target_doc.company = source_doc.company
|
||||
target_doc.is_return = 1
|
||||
|
||||
ste_doc = get_mapped_doc(
|
||||
order_doctype,
|
||||
order_doc.name,
|
||||
@@ -1219,18 +1256,13 @@ def make_return_stock_entry_for_subcontract(
|
||||
},
|
||||
},
|
||||
ignore_child_tables=True,
|
||||
postprocess=post_process,
|
||||
)
|
||||
|
||||
ste_doc.purpose = "Material Transfer"
|
||||
|
||||
if order_doctype == "Purchase Order":
|
||||
ste_doc.purchase_order = order_doc.name
|
||||
rm_detail_field = "po_detail"
|
||||
else:
|
||||
ste_doc.subcontracting_order = order_doc.name
|
||||
rm_detail_field = "sco_rm_detail"
|
||||
ste_doc.company = order_doc.company
|
||||
ste_doc.is_return = 1
|
||||
|
||||
for _key, value in available_materials.items():
|
||||
if not value.qty:
|
||||
|
||||
@@ -41,7 +41,7 @@ class calculate_taxes_and_totals:
|
||||
return items
|
||||
|
||||
def calculate(self):
|
||||
if not len(self._items):
|
||||
if not len(self.doc.items):
|
||||
return
|
||||
|
||||
self.discount_amount_applied = False
|
||||
@@ -96,7 +96,7 @@ class calculate_taxes_and_totals:
|
||||
if self.doc.get("is_return") and self.doc.get("return_against"):
|
||||
return
|
||||
|
||||
for item in self._items:
|
||||
for item in self.doc.items:
|
||||
if item.item_code and item.get("item_tax_template"):
|
||||
item_doc = frappe.get_cached_doc("Item", item.item_code)
|
||||
args = {
|
||||
@@ -155,7 +155,7 @@ class calculate_taxes_and_totals:
|
||||
return
|
||||
|
||||
if not self.discount_amount_applied:
|
||||
for item in self._items:
|
||||
for item in self.doc.items:
|
||||
self.doc.round_floats_in(item)
|
||||
|
||||
if item.discount_percentage == 100:
|
||||
@@ -259,7 +259,7 @@ class calculate_taxes_and_totals:
|
||||
if not any(cint(tax.included_in_print_rate) for tax in self.doc.get("taxes")):
|
||||
return
|
||||
|
||||
for item in self._items:
|
||||
for item in self.doc.items:
|
||||
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
||||
cumulated_tax_fraction = 0
|
||||
total_inclusive_tax_amount_per_qty = 0
|
||||
@@ -514,7 +514,7 @@ class calculate_taxes_and_totals:
|
||||
if tax.item_wise_tax_detail.get(key):
|
||||
item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
|
||||
|
||||
tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
|
||||
tax.item_wise_tax_detail[key] = [tax_rate, item_wise_tax_amount]
|
||||
|
||||
def round_off_totals(self, tax):
|
||||
if tax.account_head in frappe.flags.round_off_applicable_accounts:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.utils import add_days, getdate, nowdate
|
||||
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
@@ -46,7 +46,7 @@ def make_supplier(supplier_name, currency=None):
|
||||
return supplier_name
|
||||
|
||||
|
||||
class TestAccountsController(FrappeTestCase):
|
||||
class TestAccountsController(IntegrationTestCase):
|
||||
"""
|
||||
Test Exchange Gain/Loss booking on various scenarios.
|
||||
Test Cases are numbered for better organization
|
||||
@@ -805,7 +805,9 @@ class TestAccountsController(FrappeTestCase):
|
||||
self.assertEqual(exc_je_for_si, [])
|
||||
self.assertEqual(exc_je_for_pe, [])
|
||||
|
||||
@change_settings("Stock Settings", {"allow_internal_transfer_at_arms_length_price": 1})
|
||||
@IntegrationTestCase.change_settings(
|
||||
"Stock Settings", {"allow_internal_transfer_at_arms_length_price": 1}
|
||||
)
|
||||
def test_16_internal_transfer_at_arms_length_price(self):
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
|
||||
|
||||
class TestTaxesAndTotals(AccountsTestMixin, FrappeTestCase):
|
||||
class TestTaxesAndTotals(AccountsTestMixin, IntegrationTestCase):
|
||||
def test_distributed_discount_amount(self):
|
||||
so = make_sales_order(do_not_save=1)
|
||||
so.apply_discount_on = "Net Total"
|
||||
|
||||
@@ -2,6 +2,7 @@ import json
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code
|
||||
from erpnext.stock.doctype.item.test_item import set_item_variant_settings
|
||||
@@ -10,7 +11,7 @@ from erpnext.stock.doctype.quality_inspection.test_quality_inspection import (
|
||||
)
|
||||
|
||||
|
||||
class TestItemVariant(unittest.TestCase):
|
||||
class TestItemVariant(IntegrationTestCase):
|
||||
def test_tables_in_template_copied_to_variant(self):
|
||||
fields = [{"field_name": "quality_inspection_template"}]
|
||||
set_item_variant_settings(fields)
|
||||
|
||||
@@ -4,15 +4,16 @@ import unittest
|
||||
import frappe
|
||||
import frappe.utils
|
||||
from frappe.model import mapper
|
||||
from frappe.test_runner import make_test_records
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.utils import add_months, nowdate
|
||||
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Item"]
|
||||
|
||||
class TestMapper(unittest.TestCase):
|
||||
|
||||
class TestMapper(IntegrationTestCase):
|
||||
def test_map_docs(self):
|
||||
"""Test mapping of multiple source docs on a single target doc"""
|
||||
|
||||
make_test_records("Item")
|
||||
items = ["_Test Item", "_Test Item 2", "_Test FG Item"]
|
||||
|
||||
# Make source docs (quotations) and a target doc (sales order)
|
||||
|
||||
@@ -2,13 +2,14 @@ import unittest
|
||||
from uuid import uuid4 as _uuid4
|
||||
|
||||
import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
def uuid4():
|
||||
return str(_uuid4())
|
||||
|
||||
|
||||
class TestTaxes(unittest.TestCase):
|
||||
class TestTaxes(IntegrationTestCase):
|
||||
def setUp(self):
|
||||
self.company = frappe.get_doc(
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ import unittest
|
||||
from functools import partial
|
||||
|
||||
import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
from erpnext.controllers import queries
|
||||
|
||||
@@ -10,8 +11,11 @@ def add_default_params(func, doctype):
|
||||
return partial(func, doctype=doctype, txt="", searchfield="name", start=0, page_len=20, filters=None)
|
||||
|
||||
|
||||
class TestQueries(unittest.TestCase):
|
||||
# All tests are based on doctype/test_records.json
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = ["Employee", "Lead", "Item", "BOM", "Project", "Account"]
|
||||
|
||||
|
||||
class TestQueries(IntegrationTestCase):
|
||||
# All tests are based on self.globalTestRecords[doctype]
|
||||
|
||||
def assert_nested_in(self, item, container):
|
||||
self.assertIn(item, [vals for tuples in container for vals in tuples])
|
||||
|
||||
@@ -5,7 +5,7 @@ import copy
|
||||
from collections import defaultdict
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.utils import cint
|
||||
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||
@@ -25,7 +25,7 @@ from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order im
|
||||
)
|
||||
|
||||
|
||||
class TestSubcontractingController(FrappeTestCase):
|
||||
class TestSubcontractingController(IntegrationTestCase):
|
||||
def setUp(self):
|
||||
make_subcontracted_items()
|
||||
make_raw_materials()
|
||||
@@ -1234,6 +1234,7 @@ def make_subcontracted_items():
|
||||
"Subcontracted Item SA6": {},
|
||||
"Subcontracted Item SA7": {},
|
||||
"Subcontracted Item SA8": {},
|
||||
"Subcontracted Item SA9": {"stock_uom": "Litre"},
|
||||
}
|
||||
|
||||
for item, properties in sub_contracted_items.items():
|
||||
@@ -1254,6 +1255,7 @@ def make_raw_materials():
|
||||
"Subcontracted SRM Item 4": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
|
||||
"Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRIID.####"},
|
||||
"Subcontracted SRM Item 8": {},
|
||||
"Subcontracted SRM Item 9": {"stock_uom": "Litre"},
|
||||
}
|
||||
|
||||
for item, properties in raw_materials.items():
|
||||
@@ -1280,6 +1282,7 @@ def make_service_items():
|
||||
"Subcontracted Service Item 6": {},
|
||||
"Subcontracted Service Item 7": {},
|
||||
"Subcontracted Service Item 8": {},
|
||||
"Subcontracted Service Item 9": {},
|
||||
}
|
||||
|
||||
for item, properties in service_items.items():
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
class TestUtils(IntegrationTestCase):
|
||||
def test_reset_default_field_value(self):
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
|
||||
@@ -69,7 +69,7 @@ def get_data(filters, conditions):
|
||||
"Delivery Note",
|
||||
]:
|
||||
posting_date = "t1.posting_date"
|
||||
if filters.period_based_on:
|
||||
if filters.period_based_on and conditions.get("trans") in ["Sales Invoice", "Purchase Invoice"]:
|
||||
posting_date = "t1." + filters.period_based_on
|
||||
|
||||
if conditions["based_on_select"] in ["t1.project,", "t2.project,"]:
|
||||
@@ -224,7 +224,7 @@ def period_wise_columns_query(filters, trans):
|
||||
|
||||
if trans in ["Purchase Receipt", "Delivery Note", "Purchase Invoice", "Sales Invoice"]:
|
||||
trans_date = "posting_date"
|
||||
if filters.period_based_on:
|
||||
if filters.period_based_on and trans in ["Purchase Invoice", "Sales Invoice"]:
|
||||
trans_date = filters.period_based_on
|
||||
else:
|
||||
trans_date = "transaction_date"
|
||||
|
||||
Reference in New Issue
Block a user