diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index a4f5eb4d92d..fb48712c825 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -418,11 +418,12 @@ class Asset(AccountsController): def validate_asset_finance_books(self, row): if flt(row.expected_value_after_useful_life) >= flt(self.gross_purchase_amount): frappe.throw(_("Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount") - .format(row.idx)) + .format(row.idx), title=_("Invalid Schedule")) if not row.depreciation_start_date: if not self.available_for_use_date: - frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx)) + frappe.throw(_("Row {0}: Depreciation Start Date is required") + .format(row.idx), title=_("Invalid Schedule")) row.depreciation_start_date = get_last_day(self.available_for_use_date) if not self.is_existing_asset: @@ -440,8 +441,9 @@ class Asset(AccountsController): else: self.number_of_depreciations_booked = 0 - if cint(self.number_of_depreciations_booked) > cint(row.total_number_of_depreciations): - frappe.throw(_("Number of Depreciations Booked cannot be greater than Total Number of Depreciations")) + if flt(row.total_number_of_depreciations) <= cint(self.number_of_depreciations_booked): + frappe.throw(_("Row {0}: Total Number of Depreciations cannot be less than or equal to Number of Depreciations Booked") + .format(row.idx), title=_("Invalid Schedule")) if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(self.purchase_date): frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date") diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index a15a1b6f388..6afd6dab264 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -820,8 +820,9 @@ class TestDepreciationBasics(AssetSetup): self.assertRaises(frappe.ValidationError, asset.save) def test_number_of_depreciations(self): - """Tests if an error is raised when number_of_depreciations_booked > total_number_of_depreciations.""" + """Tests if an error is raised when number_of_depreciations_booked >= total_number_of_depreciations.""" + # number_of_depreciations_booked > total_number_of_depreciations asset = create_asset( item_code = "Macbook Pro", calculate_depreciation = 1, @@ -836,6 +837,21 @@ class TestDepreciationBasics(AssetSetup): self.assertRaises(frappe.ValidationError, asset.save) + # number_of_depreciations_booked = total_number_of_depreciations + asset_2 = create_asset( + item_code = "Macbook Pro", + calculate_depreciation = 1, + available_for_use_date = "2019-12-31", + total_number_of_depreciations = 5, + expected_value_after_useful_life = 10000, + depreciation_start_date = "2020-07-01", + opening_accumulated_depreciation = 10000, + number_of_depreciations_booked = 5, + do_not_save = 1 + ) + + self.assertRaises(frappe.ValidationError, asset_2.save) + def test_depreciation_start_date_is_before_purchase_date(self): asset = create_asset( item_code = "Macbook Pro", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index d554d52a718..3fe6b2d0d5d 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -68,6 +68,28 @@ frappe.ui.form.on('Asset Repair', { }); frappe.ui.form.on('Asset Repair Consumed Item', { + item_code: function(frm, cdt, cdn) { + var item = locals[cdt][cdn]; + + let item_args = { + 'item_code': item.item_code, + 'warehouse': frm.doc.warehouse, + 'qty': item.consumed_quantity, + 'serial_no': item.serial_no, + 'company': frm.doc.company + }; + + frappe.call({ + method: 'erpnext.stock.utils.get_incoming_rate', + args: { + args: item_args + }, + callback: function(r) { + frappe.model.set_value(cdt, cdn, 'valuation_rate', r.message); + } + }); + }, + consumed_quantity: function(frm, cdt, cdn) { var row = locals[cdt][cdn]; frappe.model.set_value(cdt, cdn, 'total_value', row.consumed_quantity * row.valuation_rate); diff --git a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json index f63add12356..4685a09db63 100644 --- a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json +++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json @@ -13,12 +13,10 @@ ], "fields": [ { - "fetch_from": "item.valuation_rate", "fieldname": "valuation_rate", "fieldtype": "Currency", "in_list_view": 1, - "label": "Valuation Rate", - "read_only": 1 + "label": "Valuation Rate" }, { "fieldname": "consumed_quantity", @@ -49,7 +47,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-11-11 18:23:00.492483", + "modified": "2022-02-08 17:37:20.028290", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair Consumed Item", diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py index a4e21579e32..14c86d56328 100644 --- a/erpnext/erpnext_integrations/taxjar_integration.py +++ b/erpnext/erpnext_integrations/taxjar_integration.py @@ -8,10 +8,6 @@ from frappe.utils import cint, flt from erpnext import get_default_company, get_region -TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head") -SHIP_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "shipping_account_head") -TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions") -TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax") SUPPORTED_COUNTRY_CODES = ["AT", "AU", "BE", "BG", "CA", "CY", "CZ", "DE", "DK", "EE", "ES", "FI", "FR", "GB", "GR", "HR", "HU", "IE", "IT", "LT", "LU", "LV", "MT", "NL", "PL", "PT", "RO", "SE", "SI", "SK", "US"] @@ -35,12 +31,14 @@ def get_client(): if api_key and api_url: client = taxjar.Client(api_key=api_key, api_url=api_url) client.set_api_config('headers', { - 'x-api-version': '2020-08-07' + 'x-api-version': '2022-01-24' }) return client def create_transaction(doc, method): + TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions") + """Create an order transaction in TaxJar""" if not TAXJAR_CREATE_TRANSACTIONS: @@ -51,6 +49,7 @@ def create_transaction(doc, method): if not client: return + TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head") sales_tax = sum([tax.tax_amount for tax in doc.taxes if tax.account_head == TAX_ACCOUNT_HEAD]) if not sales_tax: @@ -79,6 +78,7 @@ def create_transaction(doc, method): def delete_transaction(doc, method): """Delete an existing TaxJar order transaction""" + TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions") if not TAXJAR_CREATE_TRANSACTIONS: return @@ -92,6 +92,8 @@ def delete_transaction(doc, method): def get_tax_data(doc): + SHIP_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "shipping_account_head") + from_address = get_company_address_details(doc) from_shipping_state = from_address.get("state") from_country_code = frappe.db.get_value("Country", from_address.country, "code") @@ -113,20 +115,20 @@ def get_tax_data(doc): to_shipping_state = get_state_code(to_address, 'Shipping') tax_dict = { - 'from_country': from_country_code, - 'from_zip': from_address.pincode, - 'from_state': from_shipping_state, - 'from_city': from_address.city, - 'from_street': from_address.address_line1, - 'to_country': to_country_code, - 'to_zip': to_address.pincode, - 'to_city': to_address.city, - 'to_street': to_address.address_line1, - 'to_state': to_shipping_state, - 'shipping': shipping, - 'amount': doc.net_total, - 'plugin': 'erpnext', - 'line_items': line_items + "from_country": from_country_code, + "from_zip": from_address.pincode, + "from_state": from_shipping_state, + "from_city": from_address.city, + "from_street": from_address.address_line1, + "to_country": to_country_code, + "to_zip": to_address.pincode, + "to_city": to_address.city, + "to_street": to_address.address_line1, + "to_state": to_shipping_state, + "shipping": shipping, + "amount": doc.net_total, + "plugin": "erpnext", + "line_items": line_items } return tax_dict @@ -156,6 +158,9 @@ def get_line_item_dict(item, docstatus): return tax_dict def set_sales_tax(doc, method): + TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head") + TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax") + if not TAXJAR_CALCULATE_TAX: return @@ -206,6 +211,7 @@ def set_sales_tax(doc, method): doc.run_method("calculate_taxes_and_totals") def check_for_nexus(doc, tax_dict): + TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head") if not frappe.db.get_value('TaxJar Nexus', {'region_code': tax_dict["to_state"]}): for item in doc.get("items"): item.tax_collectable = flt(0) @@ -218,6 +224,8 @@ def check_for_nexus(doc, tax_dict): def check_sales_tax_exemption(doc): # if the party is exempt from sales tax, then set all tax account heads to zero + TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head") + sales_tax_exempted = hasattr(doc, "exempt_from_sales_tax") and doc.exempt_from_sales_tax \ or frappe.db.has_column("Customer", "exempt_from_sales_tax") \ and frappe.db.get_value("Customer", doc.customer, "exempt_from_sales_tax") diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index e6090ba02a2..3c406156ebd 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -62,7 +62,7 @@ class JobCard(Document): if self.get('time_logs'): for d in self.get('time_logs'): - if get_datetime(d.from_time) > get_datetime(d.to_time): + if d.to_time and get_datetime(d.from_time) > get_datetime(d.to_time): frappe.throw(_("Row {0}: From time must be less than to time").format(d.idx)) data = self.get_overlap_for(d) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 8ea791f620f..e70c5116bed 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -1265,7 +1265,7 @@ class SalarySlip(TransactionBase): for i, earning in enumerate(self.earnings): if earning.salary_component == salary_component: self.earnings[i].amount = wages_amount - self.gross_pay += self.earnings[i].amount + self.gross_pay += flt(self.earnings[i].amount, earning.precision("amount")) self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) def compute_year_to_date(self): diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index e742ae9a890..4801b333df9 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -525,6 +525,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ item.weight_per_unit = 0; item.weight_uom = ''; + item.conversion_factor = 0; if(['Sales Invoice'].includes(this.frm.doc.doctype)) { update_stock = cint(me.frm.doc.update_stock); diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index bdbf5e47f03..50d43171f80 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -57,14 +57,13 @@ class MaterialRequest(BuyingController): if actual_so_qty and (flt(so_items[so_no][item]) + already_indented > actual_so_qty): frappe.throw(_("Material Request of maximum {0} can be made for Item {1} against Sales Order {2}").format(actual_so_qty - already_indented, item, so_no)) - # Validate - # --------------------- def validate(self): super(MaterialRequest, self).validate() self.validate_schedule_date() self.check_for_on_hold_or_closed_status('Sales Order', 'sales_order') self.validate_uom_is_integer("uom", "qty") + self.validate_material_request_type() if not self.status: self.status = "Draft" @@ -84,6 +83,12 @@ class MaterialRequest(BuyingController): self.reset_default_field_value("set_warehouse", "items", "warehouse") self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse") + def validate_material_request_type(self): + """ Validate fields in accordance with selected type """ + + if self.material_request_type != "Customer Provided": + self.customer = None + def set_title(self): '''Set title as comma separated list of items''' if not self.title: diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index d46ffb56096..6e70bc579e7 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -3726,7 +3726,7 @@ Earliest Age,Frühestes Alter, Edit Details,Details bearbeiten, Edit Profile,Profil bearbeiten, Either GST Transporter ID or Vehicle No is required if Mode of Transport is Road,Bei Straßentransport ist entweder die GST-Transporter-ID oder die Fahrzeug-Nr. Erforderlich, -Email,Email, +Email,E-Mail, Email Campaigns,E-Mail-Kampagnen, Employee ID is linked with another instructor,Die Mitarbeiter-ID ist mit einem anderen Ausbilder verknüpft, Employee Tax and Benefits,Mitarbeitersteuern und -leistungen, @@ -6481,7 +6481,7 @@ Select Users,Wählen Sie Benutzer aus, Send Emails At,Die E-Mails senden um, Reminder,Erinnerung, Daily Work Summary Group User,Tägliche Arbeit Zusammenfassung Gruppenbenutzer, -email,Email, +email,E-Mail, Parent Department,Elternabteilung, Leave Block List,Urlaubssperrenliste, Days for which Holidays are blocked for this department.,"Tage, an denen eine Urlaubssperre für diese Abteilung gilt.", diff --git a/erpnext/www/lms/macros/hero.html b/erpnext/www/lms/macros/hero.html index e72bfc8175b..95ba8f7df28 100644 --- a/erpnext/www/lms/macros/hero.html +++ b/erpnext/www/lms/macros/hero.html @@ -11,7 +11,7 @@ {% if frappe.session.user == 'Guest' %} {{_('Sign Up')}} {% elif not has_access %} - + {% endif %}
@@ -20,34 +20,35 @@