mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-30 20:18:27 +00:00
chore: merge upstream
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
|
||||
frappe.provide("erpnext.accounts");
|
||||
|
||||
cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
|
||||
|
||||
erpnext.accounts.payment_triggers.setup("Purchase Invoice");
|
||||
erpnext.accounts.taxes.setup_tax_filters("Purchase Taxes and Charges");
|
||||
erpnext.accounts.taxes.setup_tax_validations("Purchase Invoice");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
|
||||
erpnext.accounts.taxes.setup_tax_validations("Purchase Taxes and Charges Template");
|
||||
erpnext.accounts.taxes.setup_tax_filters("Purchase Taxes and Charges");
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
frappe.provide("erpnext.accounts");
|
||||
|
||||
cur_frm.cscript.tax_table = "Sales Taxes and Charges";
|
||||
|
||||
erpnext.accounts.taxes.setup_tax_validations("Sales Invoice");
|
||||
erpnext.accounts.payment_triggers.setup("Sales Invoice");
|
||||
erpnext.accounts.pos.setup("Sales Invoice");
|
||||
|
||||
@@ -1588,6 +1588,12 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
self.assertEqual(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"), -1000)
|
||||
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 2500)
|
||||
|
||||
def test_zero_qty_return_invoice_with_stock_effect(self):
|
||||
cr_note = create_sales_invoice(qty=-1, rate=300, is_return=1, do_not_submit=True)
|
||||
cr_note.update_stock = True
|
||||
cr_note.items[0].qty = 0
|
||||
self.assertRaises(frappe.ValidationError, cr_note.save)
|
||||
|
||||
def test_return_invoice_with_account_mismatch(self):
|
||||
debtors2 = create_account(
|
||||
parent_account="Accounts Receivable - _TC",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
cur_frm.cscript.tax_table = "Sales Taxes and Charges";
|
||||
erpnext.accounts.taxes.setup_tax_validations("Sales Taxes and Charges Template");
|
||||
erpnext.accounts.taxes.setup_tax_filters("Sales Taxes and Charges");
|
||||
|
||||
@@ -182,8 +182,10 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
|
||||
}
|
||||
|
||||
# check invoice grand total and invoiced column's value for 3 payment terms
|
||||
si = self.create_sales_invoice(no_payment_schedule=True)
|
||||
name = si.name
|
||||
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
|
||||
si.set_posting_time = True
|
||||
si.posting_date = add_days(today(), -1)
|
||||
si.save().submit()
|
||||
|
||||
report = execute(filters)
|
||||
|
||||
@@ -207,30 +209,42 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
|
||||
|
||||
# check invoice grand total, invoiced, paid and outstanding column's value after credit note
|
||||
cr_note = self.create_credit_note(si.name, do_not_submit=True)
|
||||
cr_note.posting_date = add_days(today(), 1)
|
||||
cr_note.update_outstanding_for_self = True
|
||||
cr_note.save().submit()
|
||||
report = execute(filters)
|
||||
|
||||
expected_data_after_credit_note = [
|
||||
[100.0, 100.0, 40.0, 0.0, 60.0, self.debit_to],
|
||||
[0, 0, 100.0, 0.0, -100.0, self.debit_to],
|
||||
[100.0, 100.0, 40.0, 0.0, 60.0, si.name],
|
||||
[0, 0, 100.0, 0.0, -100.0, cr_note.name],
|
||||
]
|
||||
self.assertEqual(len(report[1]), 2)
|
||||
for i in range(2):
|
||||
row = report[1][i - 1]
|
||||
# row = report[1][0]
|
||||
self.assertEqual(
|
||||
expected_data_after_credit_note[i - 1],
|
||||
[
|
||||
row.invoice_grand_total,
|
||||
row.invoiced,
|
||||
row.paid,
|
||||
row.credit_note,
|
||||
row.outstanding,
|
||||
row.party_account,
|
||||
],
|
||||
)
|
||||
si_row = [
|
||||
[
|
||||
row.invoice_grand_total,
|
||||
row.invoiced,
|
||||
row.paid,
|
||||
row.credit_note,
|
||||
row.outstanding,
|
||||
row.voucher_no,
|
||||
]
|
||||
for row in report[1]
|
||||
if row.voucher_no == si.name
|
||||
][0]
|
||||
|
||||
cr_note_row = [
|
||||
[
|
||||
row.invoice_grand_total,
|
||||
row.invoiced,
|
||||
row.paid,
|
||||
row.credit_note,
|
||||
row.outstanding,
|
||||
row.voucher_no,
|
||||
]
|
||||
for row in report[1]
|
||||
if row.voucher_no == cr_note.name
|
||||
][0]
|
||||
self.assertEqual(expected_data_after_credit_note[0], si_row)
|
||||
self.assertEqual(expected_data_after_credit_note[1], cr_note_row)
|
||||
|
||||
def test_payment_againt_po_in_receivable_report(self):
|
||||
"""
|
||||
|
||||
@@ -152,6 +152,7 @@ class Asset(AccountsController):
|
||||
def on_submit(self):
|
||||
self.validate_in_use_date()
|
||||
self.make_asset_movement()
|
||||
self.reload()
|
||||
if not self.booked_fixed_asset and self.validate_make_gl_entry():
|
||||
self.make_gl_entries()
|
||||
if self.calculate_depreciation and not self.split_from:
|
||||
@@ -163,6 +164,7 @@ class Asset(AccountsController):
|
||||
self.validate_cancellation()
|
||||
self.cancel_movement_entries()
|
||||
self.cancel_capitalization()
|
||||
self.reload()
|
||||
self.delete_depreciation_entries()
|
||||
cancel_asset_depr_schedules(self)
|
||||
self.set_status()
|
||||
@@ -698,7 +700,9 @@ class Asset(AccountsController):
|
||||
fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account()
|
||||
|
||||
if (
|
||||
purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()
|
||||
purchase_document
|
||||
and self.purchase_receipt_amount
|
||||
and getdate(self.available_for_use_date) <= getdate()
|
||||
):
|
||||
|
||||
gl_entries.append(
|
||||
|
||||
@@ -242,9 +242,7 @@ def make_depreciation_entry(
|
||||
debit_account,
|
||||
accounting_dimensions,
|
||||
)
|
||||
frappe.db.commit()
|
||||
except Exception as e:
|
||||
frappe.db.rollback()
|
||||
depreciation_posting_error = e
|
||||
|
||||
asset.set_status()
|
||||
@@ -523,6 +521,7 @@ def depreciate_asset(asset_doc, date, notes):
|
||||
|
||||
make_depreciation_entry_for_all_asset_depr_schedules(asset_doc, date)
|
||||
|
||||
asset_doc.reload()
|
||||
cancel_depreciation_entries(asset_doc, date)
|
||||
|
||||
|
||||
|
||||
@@ -327,7 +327,7 @@ class AssetDepreciationSchedule(Document):
|
||||
schedule_date = get_last_day(schedule_date)
|
||||
|
||||
# if asset is being sold or scrapped
|
||||
if date_of_disposal:
|
||||
if date_of_disposal and getdate(schedule_date) >= getdate(date_of_disposal):
|
||||
from_date = add_months(
|
||||
getdate(asset_doc.available_for_use_date),
|
||||
(asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation),
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
frappe.provide("erpnext.buying");
|
||||
frappe.provide("erpnext.accounts.dimensions");
|
||||
|
||||
cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
|
||||
|
||||
erpnext.accounts.taxes.setup_tax_filters("Purchase Taxes and Charges");
|
||||
erpnext.accounts.taxes.setup_tax_validations("Purchase Order");
|
||||
erpnext.buying.setup_buying_controller();
|
||||
|
||||
@@ -89,6 +89,7 @@ force_item_fields = (
|
||||
"weight_per_unit",
|
||||
"weight_uom",
|
||||
"total_weight",
|
||||
"valuation_rate",
|
||||
)
|
||||
|
||||
|
||||
@@ -168,6 +169,13 @@ class AccountsController(TransactionBase):
|
||||
if not self.get("is_return") and not self.get("is_debit_note"):
|
||||
self.validate_qty_is_not_zero()
|
||||
|
||||
if (
|
||||
self.doctype in ["Sales Invoice", "Purchase Invoice"]
|
||||
and self.get("is_return")
|
||||
and self.get("update_stock")
|
||||
):
|
||||
self.validate_zero_qty_for_return_invoices_with_stock()
|
||||
|
||||
if self.get("_action") and self._action != "update_after_submit":
|
||||
self.set_missing_values(for_validate=True)
|
||||
|
||||
@@ -1044,6 +1052,18 @@ class AccountsController(TransactionBase):
|
||||
else:
|
||||
return flt(args.get(field, 0) / self.get("conversion_rate", 1))
|
||||
|
||||
def validate_zero_qty_for_return_invoices_with_stock(self):
|
||||
rows = []
|
||||
for item in self.items:
|
||||
if not flt(item.qty):
|
||||
rows.append(item)
|
||||
if rows:
|
||||
frappe.throw(
|
||||
_(
|
||||
"For Return Invoices with Stock effect, '0' qty Items are not allowed. Following rows are affected: {0}"
|
||||
).format(frappe.bold(comma_and(["#" + str(x.idx) for x in rows])))
|
||||
)
|
||||
|
||||
def validate_qty_is_not_zero(self):
|
||||
for item in self.items:
|
||||
if self.doctype == "Purchase Receipt" and item.rejected_qty:
|
||||
@@ -2708,14 +2728,20 @@ def get_advance_journal_entries(
|
||||
else:
|
||||
q = q.where(journal_acc.debit_in_account_currency > 0)
|
||||
|
||||
reference_or_condition = []
|
||||
|
||||
if include_unallocated:
|
||||
q = q.where((journal_acc.reference_name.isnull()) | (journal_acc.reference_name == ""))
|
||||
reference_or_condition.append(journal_acc.reference_name.isnull())
|
||||
reference_or_condition.append(journal_acc.reference_name == "")
|
||||
|
||||
if order_list:
|
||||
q = q.where(
|
||||
reference_or_condition.append(
|
||||
(journal_acc.reference_type == order_doctype) & ((journal_acc.reference_name).isin(order_list))
|
||||
)
|
||||
|
||||
if reference_or_condition:
|
||||
q = q.where(Criterion.any(reference_or_condition))
|
||||
|
||||
q = q.orderby(journal_entry.posting_date)
|
||||
|
||||
journal_entries = q.run(as_dict=True)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
cur_frm.cscript.tax_table = "Sales Taxes and Charges";
|
||||
|
||||
erpnext.accounts.taxes.setup_tax_validations("Sales Taxes and Charges Template");
|
||||
erpnext.accounts.taxes.setup_tax_filters("Sales Taxes and Charges");
|
||||
erpnext.pre_sales.set_as_lost("Quotation");
|
||||
|
||||
@@ -42,6 +42,39 @@ class TestQuotation(FrappeTestCase):
|
||||
|
||||
self.assertTrue(sales_order.get("payment_schedule"))
|
||||
|
||||
def test_gross_profit(self):
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.stock.get_item_details import insert_item_price
|
||||
|
||||
item_doc = make_item("_Test Item for Gross Profit", {"is_stock_item": 1})
|
||||
item_code = item_doc.name
|
||||
make_stock_entry(item_code=item_code, qty=10, rate=100, target="_Test Warehouse - _TC")
|
||||
|
||||
selling_price_list = frappe.get_all("Price List", filters={"selling": 1}, limit=1)[0].name
|
||||
frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 1)
|
||||
insert_item_price(
|
||||
frappe._dict(
|
||||
{
|
||||
"item_code": item_code,
|
||||
"price_list": selling_price_list,
|
||||
"price_list_rate": 300,
|
||||
"rate": 300,
|
||||
"conversion_factor": 1,
|
||||
"discount_amount": 0.0,
|
||||
"currency": frappe.db.get_value("Price List", selling_price_list, "currency"),
|
||||
"uom": item_doc.stock_uom,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
quotation = make_quotation(
|
||||
item_code=item_code, qty=1, rate=300, selling_price_list=selling_price_list
|
||||
)
|
||||
self.assertEqual(quotation.items[0].valuation_rate, 100)
|
||||
self.assertEqual(quotation.items[0].gross_profit, 200)
|
||||
frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 0)
|
||||
|
||||
def test_maintain_rate_in_sales_cycle_is_enforced(self):
|
||||
from erpnext.selling.doctype.quotation.quotation import make_sales_order
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
cur_frm.cscript.tax_table = "Sales Taxes and Charges";
|
||||
|
||||
erpnext.accounts.taxes.setup_tax_filters("Sales Taxes and Charges");
|
||||
erpnext.accounts.taxes.setup_tax_validations("Sales Order");
|
||||
erpnext.sales_common.setup_selling_controller();
|
||||
|
||||
@@ -123,7 +123,9 @@ class ClosingStockBalance(Document):
|
||||
)
|
||||
)
|
||||
|
||||
create_json_gz_file({"columns": columns, "data": data}, self.doctype, self.name)
|
||||
create_json_gz_file(
|
||||
{"columns": columns, "data": data}, self.doctype, self.name, "closing-stock-balance"
|
||||
)
|
||||
|
||||
def get_prepared_data(self):
|
||||
if attachments := get_attachments(self.doctype, self.name):
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
cur_frm.add_fetch("customer", "tax_id", "tax_id");
|
||||
|
||||
cur_frm.cscript.tax_table = "Sales Taxes and Charges";
|
||||
|
||||
frappe.provide("erpnext.stock");
|
||||
frappe.provide("erpnext.stock.delivery_note");
|
||||
frappe.provide("erpnext.accounts.dimensions");
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
frappe.provide("erpnext.stock");
|
||||
|
||||
cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
|
||||
|
||||
erpnext.accounts.taxes.setup_tax_filters("Purchase Taxes and Charges");
|
||||
erpnext.accounts.taxes.setup_tax_validations("Purchase Receipt");
|
||||
erpnext.buying.setup_buying_controller();
|
||||
|
||||
@@ -230,7 +230,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_queue",
|
||||
"fieldtype": "Text",
|
||||
"fieldtype": "Long Text",
|
||||
"label": "FIFO Stock Queue (qty, rate)",
|
||||
"oldfieldname": "fcfs_stack",
|
||||
"oldfieldtype": "Text",
|
||||
@@ -360,7 +360,7 @@
|
||||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-02-07 09:18:13.999231",
|
||||
"modified": "2024-03-13 09:56:13.021696",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Ledger Entry",
|
||||
|
||||
@@ -58,7 +58,7 @@ class StockLedgerEntry(Document):
|
||||
recalculate_rate: DF.Check
|
||||
serial_and_batch_bundle: DF.Link | None
|
||||
serial_no: DF.LongText | None
|
||||
stock_queue: DF.Text | None
|
||||
stock_queue: DF.LongText | None
|
||||
stock_uom: DF.Link | None
|
||||
stock_value: DF.Currency
|
||||
stock_value_difference: DF.Currency
|
||||
|
||||
Reference in New Issue
Block a user