diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml new file mode 100644 index 0000000000..4b1147e79f --- /dev/null +++ b/.github/workflows/docker-release.yml @@ -0,0 +1,14 @@ +name: Trigger Docker build on release +on: + release: + types: [released] +jobs: + curl: + runs-on: ubuntu-latest + container: + image: alpine:latest + steps: + - name: curl + run: | + apk add curl bash + curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests diff --git a/CODEOWNERS b/CODEOWNERS index 5e1113d34f..7cf65a7a73 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,17 +3,16 @@ # These owners will be the default owners for everything in # the repo. Unless a later match takes precedence, -* @nabinhait -manufacturing/ @rohitwaghchaure +manufacturing/ @rohitwaghchaure @marination accounts/ @deepeshgarg007 @nextchamp-saqib -loan_management/ @deepeshgarg007 -pos* @nextchamp-saqib -assets/ @nextchamp-saqib +loan_management/ @deepeshgarg007 @rohitwaghchaure +pos* @nextchamp-saqib @rohitwaghchaure +assets/ @nextchamp-saqib @deepeshgarg007 stock/ @marination @rohitwaghchaure -buying/ @marination @rohitwaghchaure -hr/ @Anurag810 -projects/ @hrwX -support/ @hrwX -healthcare/ @ruchamahabal -erpnext_integrations/ @Mangesh-Khairnar +buying/ @marination @deepeshgarg007 +hr/ @Anurag810 @rohitwaghchaure +projects/ @hrwX @nextchamp-saqib +support/ @hrwX @marination +healthcare/ @ruchamahabal @marination +erpnext_integrations/ @Mangesh-Khairnar @nextchamp-saqib requirements.txt @gavindsouza diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 530a6e4880..18294f3604 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '13.0.0-beta.2' +__version__ = '13.0.0-beta.3' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 421c86dba0..b2abffc79d 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -5,7 +5,22 @@ import frappe import json from frappe.utils import nowdate, add_months, get_date_str from frappe import _ -from erpnext.accounts.utils import get_fiscal_year, get_account_name +from erpnext.accounts.utils import get_fiscal_year, get_account_name, FiscalYearError + +def _get_fiscal_year(date=None): + try: + fiscal_year = get_fiscal_year(date=nowdate(), as_dict=True) + return fiscal_year + + except FiscalYearError: + #if no fiscal year for current date then get default fiscal year + try: + fiscal_year = get_fiscal_year(as_dict=True) + return fiscal_year + + except FiscalYearError: + #if still no fiscal year found then no accounting data created, return + return None def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company @@ -18,10 +33,16 @@ def get_company_for_dashboards(): return None def get_data(): + + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards() + "charts": get_charts(fiscal_year), + "number_cards": get_number_cards(fiscal_year) }) def get_dashboards(): @@ -46,10 +67,9 @@ def get_dashboards(): ] }] -def get_charts(): +def get_charts(fiscal_year): company = frappe.get_doc("Company", get_company_for_dashboards()) bank_account = company.default_bank_account or get_account_name("Bank", company=company.name) - fiscal_year = get_fiscal_year(date=nowdate()) default_cost_center = company.cost_center return [ @@ -61,8 +81,8 @@ def get_charts(): "filters_json": json.dumps({ "company": company.name, "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year[0], - "to_fiscal_year": fiscal_year[0], + "from_fiscal_year": fiscal_year.get('name'), + "to_fiscal_year": fiscal_year.get('name'), "periodicity": "Monthly", "include_default_book_entries": 1 }), @@ -158,8 +178,8 @@ def get_charts(): "report_name": "Budget Variance Report", "filters_json": json.dumps({ "company": company.name, - "from_fiscal_year": fiscal_year[0], - "to_fiscal_year": fiscal_year[0], + "from_fiscal_year": fiscal_year.get('name'), + "to_fiscal_year": fiscal_year.get('name'), "period": "Monthly", "budget_against": "Cost Center" }), @@ -190,10 +210,10 @@ def get_charts(): }, ] -def get_number_cards(): - fiscal_year = get_fiscal_year(date=nowdate()) - year_start_date = get_date_str(fiscal_year[1]) - year_end_date = get_date_str(fiscal_year[2]) +def get_number_cards(fiscal_year): + + year_start_date = get_date_str(fiscal_year.get("year_start_date")) + year_end_date = get_date_str(fiscal_year.get("year_end_date")) return [ { "doctype": "Number Card", diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 448011016e..d5ab1c1704 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -2,10 +2,11 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, cint, get_link_to_form +from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, get_first_day, cint, get_link_to_form, rounded from erpnext.accounts.utils import get_account_currency from frappe.email import sendmail_to_system_managers from frappe.utils.background_jobs import enqueue +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions def validate_service_stop_date(doc): ''' Validates service_stop_date for Purchase Invoice and Sales Invoice ''' @@ -109,6 +110,18 @@ def get_booking_dates(doc, item, posting_date=None): order by posting_date desc limit 1 ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + prev_gl_via_je = frappe.db.sql(''' + SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c + WHERE p.name = c.parent and p.company=%s and c.account=%s + and c.reference_type=%s and c.reference_name=%s + and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1 + ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + + if prev_gl_via_je: + if (not prev_gl_entry) or (prev_gl_entry and + prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date): + prev_gl_entry = prev_gl_via_je + if prev_gl_entry: start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) else: @@ -130,14 +143,48 @@ def get_booking_dates(doc, item, posting_date=None): else: return None, None, None -def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency): - if doc.doctype == "Sales Invoice": - total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency" - deferred_account = "deferred_revenue_account" - else: - total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency" - deferred_account = "deferred_expense_account" +def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency): + amount, base_amount = 0, 0 + if not last_gl_entry: + total_months = (item.service_end_date.year - item.service_start_date.year) * 12 + \ + (item.service_end_date.month - item.service_start_date.month) + 1 + + prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) \ + / flt(date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date))) + + actual_months = rounded(total_months * prorate_factor, 1) + + already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item) + base_amount = flt(item.base_net_amount / actual_months, item.precision("base_net_amount")) + + if base_amount + already_booked_amount > item.base_net_amount: + base_amount = item.base_net_amount - already_booked_amount + + if account_currency==doc.company_currency: + amount = base_amount + else: + amount = flt(item.net_amount/actual_months, item.precision("net_amount")) + if amount + already_booked_amount_in_account_currency > item.net_amount: + amount = item.net_amount - already_booked_amount_in_account_currency + + if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date): + partial_month = flt(date_diff(end_date, start_date)) \ + / flt(date_diff(get_last_day(end_date), get_first_day(start_date))) + + base_amount = rounded(partial_month, 1) * base_amount + amount = rounded(partial_month, 1) * amount + else: + already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item) + base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) + if account_currency==doc.company_currency: + amount = base_amount + else: + amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")) + + return amount, base_amount + +def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency): amount, base_amount = 0, 0 if not last_gl_entry: base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount")) @@ -146,27 +193,55 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a else: amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount")) else: - gl_entries_details = frappe.db.sql(''' - select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no - from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s - group by voucher_detail_no - '''.format(total_credit_debit, total_credit_debit_currency), - (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) - already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0 + already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item) + base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) if account_currency==doc.company_currency: amount = base_amount else: - already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0 amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")) return amount, base_amount +def get_already_booked_amount(doc, item): + if doc.doctype == "Sales Invoice": + total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency" + deferred_account = "deferred_revenue_account" + else: + total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency" + deferred_account = "deferred_expense_account" + + gl_entries_details = frappe.db.sql(''' + select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no + from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s + group by voucher_detail_no + '''.format(total_credit_debit, total_credit_debit_currency), + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + + journal_entry_details = frappe.db.sql(''' + SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no + FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and + p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s + and p.docstatus < 2 group by reference_detail_no + '''.format(total_credit_debit, total_credit_debit_currency), + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + + already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0 + already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0 + + if doc.currency == doc.company_currency: + already_booked_amount_in_account_currency = already_booked_amount + else: + already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0 + already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0 + + return already_booked_amount, already_booked_amount_in_account_currency + def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): enable_check = "enable_deferred_revenue" \ if doc.doctype=="Sales Invoice" else "enable_deferred_expense" - def _book_deferred_revenue_or_expense(item): + def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on): start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date) if not (start_date and end_date): return @@ -181,23 +256,34 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): total_days = date_diff(item.service_end_date, item.service_start_date) + 1 total_booking_days = date_diff(end_date, start_date) + 1 - amount, base_amount = calculate_amount(doc, item, last_gl_entry, - total_days, total_booking_days, account_currency) + if book_deferred_entries_based_on == 'Months': + amount, base_amount = calculate_monthly_amount(doc, item, last_gl_entry, + start_date, end_date, total_days, total_booking_days, account_currency) + else: + amount, base_amount = calculate_amount(doc, item, last_gl_entry, + total_days, total_booking_days, account_currency) - make_gl_entries(doc, credit_account, debit_account, against, - amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process) + if via_journal_entry: + book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount, + base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry) + else: + make_gl_entries(doc, credit_account, debit_account, against, + amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process) # Returned in case of any errors because it tries to submit the same record again and again in case of errors if frappe.flags.deferred_accounting_error: return if getdate(end_date) < getdate(posting_date) and not last_gl_entry: - _book_deferred_revenue_or_expense(item) + _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on) + via_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_via_journal_entry')) + submit_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'submit_journal_entries')) + book_deferred_entries_based_on = frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_based_on') for item in doc.get('items'): if item.get(enable_check): - _book_deferred_revenue_or_expense(item) + _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on) def process_deferred_accounting(posting_date=None): ''' Converts deferred income/expense into income/expense @@ -281,3 +367,83 @@ def send_mail(deferred_process): and submit manually after resolving errors """).format(get_link_to_form('Process Deferred Accounting', deferred_process)) sendmail_to_system_managers(title, content) + +def book_revenue_via_journal_entry(doc, credit_account, debit_account, against, + amount, base_amount, posting_date, project, account_currency, cost_center, item, + deferred_process=None, submit='No'): + + if amount == 0: return + + journal_entry = frappe.new_doc('Journal Entry') + journal_entry.posting_date = posting_date + journal_entry.company = doc.company + journal_entry.voucher_type = 'Deferred Revenue' if doc.doctype == 'Sales Invoice' \ + else 'Deferred Expense' + + debit_entry = { + 'account': credit_account, + 'credit': base_amount, + 'credit_in_account_currency': amount, + 'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier', + 'party': against, + 'account_currency': account_currency, + 'reference_name': doc.name, + 'reference_type': doc.doctype, + 'reference_detail_no': item.name, + 'cost_center': cost_center, + 'project': project, + } + + credit_entry = { + 'account': debit_account, + 'debit': base_amount, + 'debit_in_account_currency': amount, + 'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier', + 'party': against, + 'account_currency': account_currency, + 'reference_name': doc.name, + 'reference_type': doc.doctype, + 'reference_detail_no': item.name, + 'cost_center': cost_center, + 'project': project, + } + + for dimension in get_accounting_dimensions(): + debit_entry.update({ + dimension: item.get(dimension) + }) + + credit_entry.update({ + dimension: item.get(dimension) + }) + + journal_entry.append('accounts', debit_entry) + journal_entry.append('accounts', credit_entry) + + try: + journal_entry.save() + + if submit: + journal_entry.submit() + except: + frappe.db.rollback() + traceback = frappe.get_traceback() + frappe.log_error(message=traceback) + + frappe.flags.deferred_accounting_error = True + +def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr): + + if doctype == 'Sales Invoice': + credit_account, debit_account = frappe.db.get_value('Sales Invoice Item', {'name': voucher_detail_no}, + ['income_account', 'deferred_revenue_account']) + else: + credit_account, debit_account = frappe.db.get_value('Purchase Invoice Item', {'name': voucher_detail_no}, + ['deferred_expense_account', 'expense_account']) + + if dr_or_cr == 'Debit': + return debit_account + else: + return credit_account + + diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 42fb9f4f37..31315e4c71 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Accounts Receivable", - "links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Billed\",\n \"name\": \"Ordered Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -98,7 +98,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-05-27 20:34:50.949772", + "modified": "2020-06-19 12:42:44.054598", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -122,11 +122,6 @@ "link_to": "Purchase Invoice", "type": "DocType" }, - { - "label": "Dashboard", - "link_to": "Accounts", - "type": "Dashboard" - }, { "label": "Journal Entry", "link_to": "Journal Entry", @@ -151,6 +146,11 @@ "label": "Trial Balance", "link_to": "Trial Balance", "type": "Report" + }, + { + "label": "Dashboard", + "link_to": "Accounts", + "type": "Dashboard" } ] } \ No newline at end of file diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index af252e6191..d2659d429b 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -34,11 +34,15 @@ { "fieldname": "properties", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break0", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -49,7 +53,9 @@ "no_copy": 1, "oldfieldname": "account_name", "oldfieldtype": "Data", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "account_number", @@ -57,13 +63,17 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Account Number", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_group", "fieldtype": "Check", - "label": "Is Group" + "label": "Is Group", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -75,7 +85,9 @@ "options": "Company", "read_only": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "root_type", @@ -83,7 +95,9 @@ "in_standard_filter": 1, "label": "Root Type", "options": "\nAsset\nLiability\nIncome\nExpense\nEquity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "report_type", @@ -91,24 +105,32 @@ "in_standard_filter": 1, "label": "Report Type", "options": "\nBalance Sheet\nProfit and Loss", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_group==0", "fieldname": "account_currency", "fieldtype": "Link", "label": "Currency", - "options": "Currency" + "options": "Currency", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "inter_company_account", "fieldtype": "Check", - "label": "Inter Company Account" + "label": "Inter Company Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -120,7 +142,9 @@ "oldfieldtype": "Link", "options": "Account", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Setting Account Type helps in selecting this Account in transactions.", @@ -130,7 +154,9 @@ "label": "Account Type", "oldfieldname": "account_type", "oldfieldtype": "Select", - "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nTax\nTemporary" + "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary", + "show_days": 1, + "show_seconds": 1 }, { "description": "Rate at which this tax is applied", @@ -138,7 +164,9 @@ "fieldtype": "Float", "label": "Rate", "oldfieldname": "tax_rate", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "show_days": 1, + "show_seconds": 1 }, { "description": "If the account is frozen, entries are allowed to restricted users.", @@ -147,13 +175,17 @@ "label": "Frozen", "oldfieldname": "freeze_account", "oldfieldtype": "Select", - "options": "No\nYes" + "options": "No\nYes", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "balance_must_be", "fieldtype": "Select", "label": "Balance must be", - "options": "\nDebit\nCredit" + "options": "\nDebit\nCredit", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "lft", @@ -162,7 +194,9 @@ "label": "Lft", "print_hide": 1, "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rgt", @@ -171,7 +205,9 @@ "label": "Rgt", "print_hide": 1, "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "old_parent", @@ -179,27 +215,33 @@ "hidden": 1, "label": "Old Parent", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "eval:(doc.report_type == 'Profit and Loss' && !doc.is_group)", "fieldname": "include_in_gross", "fieldtype": "Check", - "label": "Include in gross" + "label": "Include in gross", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disabled", "fieldtype": "Check", - "label": "Disable" + "label": "Disable", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-money", "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-03-18 17:57:52.063233", + "modified": "2020-06-11 15:15:54.338622", "modified_by": "Administrator", "module": "Accounts", "name": "Account", diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index f62d07668d..28b090bdad 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = { on_change: function() { var me = frappe.treeview_settings['Account'].treeview; var company = me.page.fields_dict.company.get_value(); + if (!company) { + frappe.throw(__("Please set a Company")); + } frappe.call({ method: "erpnext.accounts.doctype.account.account.get_root_company", args: { diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 894ec5bdec..8834385135 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -72,7 +72,11 @@ def make_dimension_in_accounting_doctypes(doc): if doctype == "Budget": add_dimension_to_budget_doctype(df, doc) else: - create_custom_field(doctype, df) + meta = frappe.get_meta(doctype, cached=False) + fieldnames = [d.fieldname for d in meta.get("fields")] + + if df['fieldname'] not in fieldnames: + create_custom_field(doctype, df) count += 1 diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 353ff77223..e4b96ae67e 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -22,7 +22,12 @@ "allow_cost_center_in_entry_of_bs_account", "add_taxes_from_item_tax_template", "automatically_fetch_payment_terms", + "deferred_accounting_settings_section", "automatically_process_deferred_accounting_entry", + "book_deferred_entries_based_on", + "column_break_18", + "book_deferred_entries_via_journal_entry", + "submit_journal_entries", "print_settings", "show_inclusive_tax_in_print", "column_break_12", @@ -189,13 +194,45 @@ "fieldname": "automatically_process_deferred_accounting_entry", "fieldtype": "Check", "label": "Automatically Process Deferred Accounting Entry" + }, + { + "fieldname": "deferred_accounting_settings_section", + "fieldtype": "Section Break", + "label": "Deferred Accounting Settings" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If this is unchecked direct GL Entries will be created to book Deferred Revenue/Expense", + "fieldname": "book_deferred_entries_via_journal_entry", + "fieldtype": "Check", + "label": "Book Deferred Entries Via Journal Entry" + }, + { + "default": "0", + "depends_on": "eval:doc.book_deferred_entries_via_journal_entry", + "description": "If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually", + "fieldname": "submit_journal_entries", + "fieldtype": "Check", + "label": "Submit Journal Entries" + }, + { + "default": "Days", + "description": "If \"Months\" is selected then fixed amount will be booked as deferred revenue or expense for each month irrespective of number of days in a month. Will be prorated if deferred revenue or expense is not booked for an entire month.", + "fieldname": "book_deferred_entries_based_on", + "fieldtype": "Select", + "label": "Book Deferred Entries Based On", + "options": "Days\nMonths" } ], "icon": "icon-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2019-12-19 16:58:17.395595", + "modified": "2020-06-22 20:13:26.043092", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js index 9e2f6eed3b..ee23b1be5c 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.js +++ b/erpnext/accounts/doctype/cost_center/cost_center.js @@ -14,7 +14,18 @@ frappe.ui.form.on('Cost Center', { is_group: 1 } } - }) + }); + + frm.set_query("cost_center", "distributed_cost_center", function() { + return { + filters: { + company: frm.doc.company, + is_group: 0, + enable_distributed_cost_center: 0, + name: ['!=', frm.doc.name] + } + }; + }); }, refresh: function(frm) { if (!frm.is_new()) { @@ -60,8 +71,13 @@ frappe.ui.form.on('Cost Center', { "label": "Cost Center Number", "fieldname": "cost_center_number", "fieldtype": "Data", - "reqd": 1, "default": frm.doc.cost_center_number + }, + { + "label": __("Merge with existing"), + "fieldname": "merge", + "fieldtype": "Check", + "default": 0 } ], primary_action: function() { @@ -76,8 +92,9 @@ frappe.ui.form.on('Cost Center', { args: { docname: frm.doc.name, cost_center_name: data.cost_center_name, - cost_center_number: data.cost_center_number, - company: frm.doc.company + cost_center_number: cstr(data.cost_center_number), + company: frm.doc.company, + merge: data.merge }, callback: function(r) { frappe.dom.unfreeze(); diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 5013c92a32..e7fa954e01 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -16,6 +16,9 @@ "cb0", "is_group", "disabled", + "section_break_9", + "enable_distributed_cost_center", + "distributed_cost_center", "lft", "rgt", "old_parent" @@ -119,13 +122,31 @@ "fieldname": "disabled", "fieldtype": "Check", "label": "Disabled" + }, + { + "default": "0", + "fieldname": "enable_distributed_cost_center", + "fieldtype": "Check", + "label": "Enable Distributed Cost Center" + }, + { + "depends_on": "eval:doc.is_group==0", + "fieldname": "section_break_9", + "fieldtype": "Section Break" + }, + { + "depends_on": "enable_distributed_cost_center", + "fieldname": "distributed_cost_center", + "fieldtype": "Table", + "label": "Distributed Cost Center", + "options": "Distributed Cost Center" } ], "icon": "fa fa-money", "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-04-29 16:09:30.025214", + "modified": "2020-06-17 16:09:30.025214", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py index 0294e78111..12094d4f98 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.py +++ b/erpnext/accounts/doctype/cost_center/cost_center.py @@ -19,6 +19,24 @@ class CostCenter(NestedSet): def validate(self): self.validate_mandatory() self.validate_parent_cost_center() + self.validate_distributed_cost_center() + + def validate_distributed_cost_center(self): + if cint(self.enable_distributed_cost_center): + if not self.distributed_cost_center: + frappe.throw(_("Please enter distributed cost center")) + if sum(x.percentage_allocation for x in self.distributed_cost_center) != 100: + frappe.throw(_("Total percentage allocation for distributed cost center should be equal to 100")) + if not self.get('__islocal'): + if not cint(frappe.get_cached_value("Cost Center", {"name": self.name}, "enable_distributed_cost_center")) \ + and self.check_if_part_of_distributed_cost_center(): + frappe.throw(_("Cannot enable Distributed Cost Center for a Cost Center already allocated in another Distributed Cost Center")) + if next((True for x in self.distributed_cost_center if x.cost_center == x.parent), False): + frappe.throw(_("Parent Cost Center cannot be added in Distributed Cost Center")) + if check_if_distributed_cost_center_enabled(list(x.cost_center for x in self.distributed_cost_center)): + frappe.throw(_("A Distributed Cost Center cannot be added in the Distributed Cost Center allocation table.")) + else: + self.distributed_cost_center = [] def validate_mandatory(self): if self.cost_center_name != self.company and not self.parent_cost_center: @@ -43,12 +61,15 @@ class CostCenter(NestedSet): return 1 def convert_ledger_to_group(self): + if cint(self.enable_distributed_cost_center): + frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group")) + if self.check_if_part_of_distributed_cost_center(): + frappe.throw(_("Cost Center Already Allocated in a Distributed Cost Center cannot be converted to group")) if self.check_gle_exists(): frappe.throw(_("Cost Center with existing transactions can not be converted to group")) - else: - self.is_group = 1 - self.save() - return 1 + self.is_group = 1 + self.save() + return 1 def check_gle_exists(self): return frappe.db.get_value("GL Entry", {"cost_center": self.name}) @@ -57,6 +78,9 @@ class CostCenter(NestedSet): return frappe.db.sql("select name from `tabCost Center` where \ parent_cost_center = %s and docstatus != 2", self.name) + def check_if_part_of_distributed_cost_center(self): + return frappe.db.get_value("Distributed Cost Center", {"cost_center": self.name}) + def before_rename(self, olddn, newdn, merge=False): # Add company abbr if not provided from erpnext.setup.doctype.company.company import get_name_with_abbr @@ -100,3 +124,7 @@ def get_name_with_number(new_account, account_number): if account_number and not new_account[0].isdigit(): new_account = account_number + " - " + new_account return new_account + +def check_if_distributed_cost_center_enabled(cost_center_list): + value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1) + return next((True for x in value_list if x[0]), False) \ No newline at end of file diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py index 8f23d90676..b5fc7e3b49 100644 --- a/erpnext/accounts/doctype/cost_center/test_cost_center.py +++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py @@ -22,6 +22,33 @@ class TestCostCenter(unittest.TestCase): self.assertRaises(frappe.ValidationError, cost_center.save) + def test_validate_distributed_cost_center(self): + + if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center - _TC'}): + frappe.get_doc(test_records[0]).insert() + + if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}): + frappe.get_doc(test_records[1]).insert() + + invalid_distributed_cost_center = frappe.get_doc({ + "company": "_Test Company", + "cost_center_name": "_Test Distributed Cost Center", + "doctype": "Cost Center", + "is_group": 0, + "parent_cost_center": "_Test Company - _TC", + "enable_distributed_cost_center": 1, + "distributed_cost_center": [{ + "cost_center": "_Test Cost Center - _TC", + "percentage_allocation": 40 + }, { + "cost_center": "_Test Cost Center 2 - _TC", + "percentage_allocation": 50 + } + ] + }) + + self.assertRaises(frappe.ValidationError, invalid_distributed_cost_center.save) + def create_cost_center(**args): args = frappe._dict(args) if args.cost_center_name: diff --git a/erpnext/accounts/report/ordered_items_to_be_billed/__init__.py b/erpnext/accounts/doctype/distributed_cost_center/__init__.py similarity index 100% rename from erpnext/accounts/report/ordered_items_to_be_billed/__init__.py rename to erpnext/accounts/doctype/distributed_cost_center/__init__.py diff --git a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json new file mode 100644 index 0000000000..45b0e2df9b --- /dev/null +++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2020-03-19 12:34:01.500390", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "cost_center", + "percentage_allocation" + ], + "fields": [ + { + "fieldname": "cost_center", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Cost Center", + "options": "Cost Center", + "reqd": 1 + }, + { + "fieldname": "percentage_allocation", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Percentage Allocation", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-03-19 12:54:43.674655", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Distributed Cost Center", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py new file mode 100644 index 0000000000..48c589f0c0 --- /dev/null +++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class DistributedCostCenter(Document): + pass diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 291aff3f5a..645da341a3 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ -from frappe.utils import flt, fmt_money, getdate, formatdate +from frappe.utils import flt, fmt_money, getdate, formatdate, cint from frappe.model.document import Document from frappe.model.naming import set_name_from_naming_options from frappe.model.meta import get_field_precision @@ -134,10 +134,17 @@ class GLEntry(Document): return self.cost_center_company[self.cost_center] + def _check_is_group(): + return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group')) + if self.cost_center and _get_cost_center_company() != self.company: frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}") .format(self.voucher_type, self.voucher_no, self.cost_center, self.company)) + if self.cost_center and _check_is_group(): + frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot + be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))) + def validate_party(self): validate_party_frozen_disabled(self.party_type, self.party) diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js index 0fab8b70f4..db4f7c423f 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js @@ -188,14 +188,15 @@ frappe.ui.form.on('Invoice Discounting', { }, show_general_ledger: (frm) => { - if(frm.doc.docstatus===1) { + if(frm.doc.docstatus > 0) { cur_frm.add_custom_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: frm.doc.name, from_date: frm.doc.posting_date, - to_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), company: frm.doc.company, - group_by: "Group by Voucher (Consolidated)" + group_by: "Group by Voucher (Consolidated)", + show_cancelled_entries: frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __("View")); diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 9a832e3c1f..a09face791 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -13,15 +13,16 @@ frappe.ui.form.on("Journal Entry", { refresh: function(frm) { erpnext.toggle_naming_series(); - if(frm.doc.docstatus==1) { + if(frm.doc.docstatus > 0) { frm.add_custom_button(__('Ledger'), function() { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, + "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), "company": frm.doc.company, "finance_book": frm.doc.finance_book, - "group_by_voucher": 0 + "group_by": '', + "show_cancelled_entries": frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __('View')); @@ -278,7 +279,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ // payroll entry if(jvd.reference_type==="Payroll Entry") { return { - query: "erpnext.hr.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv", + query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv", }; } diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 9d5063929f..4573c50134 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -83,7 +83,7 @@ "label": "Entry Type", "oldfieldname": "voucher_type", "oldfieldtype": "Select", - "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation", + "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense", "reqd": 1, "search_index": 1 }, @@ -191,6 +191,7 @@ { "fieldname": "total_debit", "fieldtype": "Currency", + "in_list_view": 1, "label": "Total Debit", "no_copy": 1, "oldfieldname": "total_debit", @@ -252,7 +253,6 @@ "fieldname": "total_amount", "fieldtype": "Currency", "hidden": 1, - "in_list_view": 1, "label": "Total Amount", "no_copy": 1, "options": "total_amount_currency", @@ -503,7 +503,7 @@ "idx": 176, "is_submittable": 1, "links": [], - "modified": "2020-04-29 10:55:28.240916", + "modified": "2020-06-02 18:15:46.955697", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 41922a2a69..7360b39942 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -10,6 +10,7 @@ from erpnext.accounts.utils import get_balance_on, get_account_currency from erpnext.accounts.party import get_party_account from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting +from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts from six import string_types, iteritems @@ -54,7 +55,7 @@ class JournalEntry(AccountsController): def on_cancel(self): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries - from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip + from erpnext.payroll.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip unlink_ref_doc_from_payment_entries(self) unlink_ref_doc_from_salary_slip(self.name) self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') @@ -265,7 +266,10 @@ class JournalEntry(AccountsController): # set totals if not d.reference_name in self.reference_totals: self.reference_totals[d.reference_name] = 0.0 - self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr)) + + if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'): + self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr)) + self.reference_types[d.reference_name] = d.reference_type self.reference_accounts[d.reference_name] = d.account @@ -277,10 +281,16 @@ class JournalEntry(AccountsController): # check if party and account match if d.reference_type in ("Sales Invoice", "Purchase Invoice"): - if d.reference_type == "Sales Invoice": - party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] + if self.voucher_type in ('Deferred Revenue', 'Deferred Expense') and d.reference_detail_no: + debit_or_credit = 'Debit' if d.debit else 'Credit' + party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no, + debit_or_credit) else: - party_account = against_voucher[1] + if d.reference_type == "Sales Invoice": + party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] + else: + party_account = against_voucher[1] + if (against_voucher[0] != d.party or party_account != d.account): frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}") .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1], @@ -513,14 +523,20 @@ class JournalEntry(AccountsController): "against_voucher_type": d.reference_type, "against_voucher": d.reference_name, "remarks": remarks, + "voucher_detail_no": d.reference_detail_no, "cost_center": d.cost_center, "project": d.project, "finance_book": self.finance_book }, item=d) ) + if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'): + update_outstanding = 'No' + else: + update_outstanding = 'Yes' + if gl_map: - make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj) + make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding) def get_balance(self): if not self.get('accounts'): diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index 26c84a6398..a89fefde07 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -18,6 +18,7 @@ "accounting_dimensions_section", "cost_center", "dimension_col_break", + "project", "currency_section", "account_currency", "column_break_10", @@ -32,7 +33,7 @@ "reference_type", "reference_name", "reference_due_date", - "project", + "reference_detail_no", "col_break3", "is_advance", "user_remark", @@ -268,12 +269,18 @@ "fieldtype": "Link", "label": "Bank Account", "options": "Bank Account" + }, + { + "fieldname": "reference_detail_no", + "fieldtype": "Data", + "hidden": 1, + "label": "Reference Detail No" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-25 01:47:49.060128", + "modified": "2020-06-26 14:06:54.833738", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index a378a51cdf..42c9fdeba4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -172,8 +172,8 @@ frappe.ui.form.on('Payment Entry', { frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency); frm.toggle_display("base_received_amount", ( - frm.doc.paid_to_account_currency != company_currency - && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency + frm.doc.paid_to_account_currency != company_currency + && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency && frm.doc.base_paid_amount != frm.doc.base_received_amount )); @@ -234,14 +234,15 @@ frappe.ui.form.on('Payment Entry', { }, show_general_ledger: function(frm) { - if(frm.doc.docstatus==1) { + if(frm.doc.docstatus > 0) { frm.add_custom_button(__('Ledger'), function() { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, + "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), "company": frm.doc.company, - group_by: "" + "group_by": "", + "show_cancelled_entries": frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, "fa fa-table"); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d2245d6a6d..59611bc74c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -319,7 +319,7 @@ class PaymentEntry(AccountsController): invoice_payment_amount_map.setdefault(key, 0.0) invoice_payment_amount_map[key] += reference.allocated_amount - if not invoice_paid_amount_map.get(reference.reference_name): + if not invoice_paid_amount_map.get(key): payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name}, fields=['paid_amount', 'payment_amount', 'payment_term']) for term in payment_schedule: @@ -332,12 +332,14 @@ class PaymentEntry(AccountsController): frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) else: - outstanding = invoice_paid_amount_map.get(key)['outstanding'] + outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding')) + if amount > outstanding: frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0])) - frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s - WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) + if amount and outstanding: + frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s + WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) def set_status(self): if self.docstatus == 2: @@ -451,6 +453,8 @@ class PaymentEntry(AccountsController): frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) def set_remarks(self): + if self.remarks: return + if self.payment_type=="Internal Transfer": remarks = [_("Amount {0} {1} transferred from {2} to {3}") .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] @@ -1091,17 +1095,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount): references = [] for payment_term in payment_schedule: - references.append({ - 'reference_doctype': dt, - 'reference_name': dn, - 'bill_no': doc.get('bill_no'), - 'due_date': doc.get('due_date'), - 'total_amount': grand_total, - 'outstanding_amount': outstanding_amount, - 'payment_term': payment_term.payment_term, - 'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount, + payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount, payment_term.precision('payment_amount')) - }) + + if payment_term_outstanding: + references.append({ + 'reference_doctype': dt, + 'reference_name': dn, + 'bill_no': doc.get('bill_no'), + 'due_date': doc.get('due_date'), + 'total_amount': grand_total, + 'outstanding_amount': outstanding_amount, + 'payment_term': payment_term.payment_term, + 'allocated_amount': payment_term_outstanding + }) return references diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index 7508683c08..eef6be1a7a 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -349,9 +349,10 @@ "read_only": 1 } ], + "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2020-05-08 10:23:02.815237", + "modified": "2020-05-29 17:38:49.392713", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js index 87e02fef1b..e923d4ed5e 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js @@ -25,9 +25,10 @@ frappe.ui.form.on('Period Closing Voucher', { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, + "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), "company": frm.doc.company, - group_by_voucher: 0 + "group_by": "", + "show_cancelled_entries": frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, "fa fa-table"); diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 4d9053a55b..ead300e3bb 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -17,6 +17,8 @@ from six import string_types apply_on_dict = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"} +other_fields = ["other_item_code", "other_item_group", "other_brand"] + class PricingRule(Document): def validate(self): self.validate_mandatory() @@ -47,6 +49,13 @@ class PricingRule(Document): if tocheck and not self.get(tocheck): throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError) + if self.apply_rule_on_other: + o_field = 'other_' + frappe.scrub(self.apply_rule_on_other) + if not self.get(o_field) and o_field in other_fields: + frappe.throw(_("For the 'Apply Rule On Other' condition the field {0} is mandatory") + .format(frappe.bold(self.apply_rule_on_other))) + + if self.price_or_product_discount == 'Price' and not self.rate_or_discount: throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError) @@ -80,13 +89,27 @@ class PricingRule(Document): for f in options: if not f: continue - f = frappe.scrub(f) - if f!=fieldname: - self.set(f, None) + scrubbed_f = frappe.scrub(f) + + if logic_field == 'apply_on': + apply_on_f = apply_on_dict.get(f, f) + else: + apply_on_f = scrubbed_f + + if scrubbed_f != fieldname: + self.set(apply_on_f, None) if self.mixed_conditions and self.get("same_item"): self.same_item = 0 + apply_rule_on_other = frappe.scrub(self.apply_rule_on_other or "") + + cleanup_other_fields = (other_fields if not apply_rule_on_other + else [o_field for o_field in other_fields if o_field != 'other_' + apply_rule_on_other]) + + for other_field in cleanup_other_fields: + self.set(other_field, None) + def validate_rate_or_discount(self): for field in ["Rate"]: if flt(self.get(frappe.scrub(field))) < 0: diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 2da71dfd0e..2bf0b72563 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -385,6 +385,50 @@ class TestPricingRule(unittest.TestCase): so.load_from_db() self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].item_code, "_Test Item 2") + + def test_cumulative_pricing_rule(self): + frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule') + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Cumulative Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [{ + "item_code": "_Test Item", + }], + "is_cumulative": 1, + "selling": 1, + "applicable_for": "Customer", + "customer": "_Test Customer", + "rate_or_discount": "Discount Percentage", + "rate": 0, + "min_amt": 0, + "max_amt": 10000, + "discount_percentage": 17.5, + "price_or_product_discount": "Price", + "company": "_Test Company", + "valid_from": frappe.utils.nowdate(), + "valid_upto": frappe.utils.nowdate() + } + frappe.get_doc(test_record.copy()).insert() + + args = frappe._dict({ + "item_code": "_Test Item", + "company": "_Test Company", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Sales Invoice", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "order_type": "Sales", + "customer": "_Test Customer", + "name": None, + "transaction_date": frappe.utils.nowdate() + }) + details = get_item_details(args) + + self.assertTrue(details) def make_pricing_rule(**args): args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index cb05481df5..53115f92d0 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -366,8 +366,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]): sum_qty, sum_amt = [0, 0] doctype = doc.get('parenttype') or doc.doctype - date_field = ('transaction_date' - if doc.get('transaction_date') else 'posting_date') + date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date' child_doctype = '{0} Item'.format(doctype) apply_on = frappe.scrub(pr_doc.get('apply_on')) diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js index 975c60cf91..2800c195ce 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js +++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js @@ -10,6 +10,18 @@ frappe.ui.form.on('Process Deferred Accounting', { } }; }); + + if (frm.doc.company) { + frm.set_query("account", function() { + return { + filters: { + 'company': frm.doc.company, + 'root_type': 'Liability', + 'is_group': 0 + } + }; + }); + } }, validate: function() { diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 98ba5c72ae..829c34da67 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -42,6 +42,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -168,7 +170,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -180,7 +184,9 @@ "options": "ACC-PINV-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier", @@ -192,7 +198,9 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -204,7 +212,9 @@ "label": "Supplier Name", "oldfieldname": "supplier_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "supplier.tax_id", @@ -212,21 +222,27 @@ "fieldtype": "Read Only", "label": "Tax Id", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "due_date", "fieldtype": "Date", "label": "Due Date", "oldfieldname": "due_date", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_paid", "fieldtype": "Check", "label": "Is Paid", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -234,19 +250,25 @@ "fieldtype": "Check", "label": "Is Return (Debit Note)", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply Tax Withholding Amount", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -256,13 +278,17 @@ "label": "Company", "options": "Company", "print_hide": 1, - "remember_last_selected_value": 1 + "remember_last_selected_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "default": "Today", @@ -274,7 +300,9 @@ "oldfieldtype": "Date", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "posting_time", @@ -283,6 +311,8 @@ "no_copy": 1, "print_hide": 1, "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -291,7 +321,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -303,44 +335,58 @@ "oldfieldtype": "Link", "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:doc.on_hold", "fieldname": "sb_14", "fieldtype": "Section Break", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "on_hold", "fieldtype": "Check", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "description": "Once set, this invoice will be on hold till the set date", "fieldname": "release_date", "fieldtype": "Date", - "label": "Release Date" + "label": "Release Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_17", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "fieldname": "hold_comment", "fieldtype": "Small Text", - "label": "Reason For Putting On Hold" + "label": "Reason For Putting On Hold", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details" + "label": "Supplier Invoice Details", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -348,11 +394,15 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -360,13 +410,17 @@ "label": "Supplier Invoice Date", "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", "fieldname": "returns", "fieldtype": "Section Break", - "label": "Returns" + "label": "Returns", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", @@ -376,26 +430,34 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", @@ -403,51 +465,67 @@ "in_global_search": 1, "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", "fieldtype": "Small Text", "label": "Contact Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -456,7 +534,9 @@ "oldfieldname": "currency", "oldfieldtype": "Select", "options": "Currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -465,18 +545,24 @@ "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -484,14 +570,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -500,11 +590,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -512,7 +606,9 @@ "fieldtype": "Link", "label": "Set Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -522,11 +618,15 @@ "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -534,7 +634,9 @@ "fieldtype": "Select", "label": "Raw Materials Supplied", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", @@ -545,25 +647,33 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "update_stock", "fieldtype": "Check", "label": "Update Stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -573,42 +683,56 @@ "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Purchase Invoice Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", "fieldtype": "Table", "label": "Supplied Items", "options": "Purchase Receipt Item Supplied", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_26", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -616,7 +740,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -626,18 +752,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_28", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -647,42 +779,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_49", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_51", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -691,7 +837,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -699,13 +847,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -714,13 +866,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -730,7 +886,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -740,7 +898,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -750,11 +910,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_40", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -764,7 +928,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -774,7 +940,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -782,14 +950,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -797,7 +969,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -805,28 +979,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_46", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_49", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -836,7 +1020,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -845,7 +1031,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -855,7 +1043,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -864,13 +1054,17 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break8", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -881,7 +1075,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -890,7 +1086,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -900,7 +1098,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -909,7 +1109,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_advance", @@ -920,7 +1122,9 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "outstanding_amount", @@ -931,14 +1135,18 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "grand_total", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -946,30 +1154,40 @@ "depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)", "fieldname": "payments_section", "fieldtype": "Section Break", - "label": "Payments" + "label": "Payments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", "options": "Mode of Payment", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cash_bank_account", "fieldtype": "Link", "label": "Cash/Bank Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "clearance_date", "fieldtype": "Date", "hidden": 1, - "label": "Clearance Date" + "label": "Clearance Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_br_payments", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_paid", @@ -978,7 +1196,9 @@ "label": "Paid Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_paid_amount", @@ -987,7 +1207,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -995,7 +1217,9 @@ "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", - "label": "Write Off" + "label": "Write Off", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "write_off_amount", @@ -1003,7 +1227,9 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_write_off_amount", @@ -1012,11 +1238,15 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_61", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1024,7 +1254,9 @@ "fieldtype": "Link", "label": "Write Off Account", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1032,7 +1264,9 @@ "fieldtype": "Link", "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1042,13 +1276,17 @@ "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "allocate_advances_automatically", "fieldtype": "Check", - "label": "Set Advances and Allocate (FIFO)" + "label": "Set Advances and Allocate (FIFO)", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.allocate_advances_automatically", @@ -1056,7 +1294,9 @@ "fieldtype": "Button", "label": "Get Advances Paid", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advances", @@ -1066,20 +1306,26 @@ "oldfieldname": "advance_allocation_details", "oldfieldtype": "Table", "options": "Purchase Invoice Advance", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -1087,7 +1333,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1095,25 +1343,33 @@ "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", "fieldtype": "Link", "label": "Terms", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1" + "label": "Terms and Conditions1", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1121,7 +1377,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1129,11 +1387,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_112", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1145,14 +1407,18 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1161,7 +1427,9 @@ "label": "More Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "credit_to", @@ -1172,7 +1440,9 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -1182,7 +1452,9 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -1192,7 +1464,9 @@ "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "against_expense_account", @@ -1202,11 +1476,15 @@ "no_copy": 1, "oldfieldname": "against_expense_account", "oldfieldtype": "Small Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_63", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -1215,14 +1493,18 @@ "in_standard_filter": 1, "label": "Status", "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_invoice_reference", "fieldtype": "Link", "label": "Inter Company Invoice Reference", "options": "Sales Invoice", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", @@ -1231,14 +1513,18 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1247,7 +1533,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1256,11 +1544,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_114", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1269,24 +1561,32 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions " + "label": "Accounting Dimensions ", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1294,7 +1594,9 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_withholding_category", @@ -1302,14 +1604,32 @@ "hidden": 1, "label": "Tax Withholding Category", "options": "Tax Withholding Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-04-18 13:05:25.199832", + "modified": "2020-06-13 22:26:30.800199", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index aa1d5b526c..3cd57d403a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -238,6 +238,12 @@ class PurchaseInvoice(BuyingController): not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")): if self.update_stock and (not item.from_warehouse): + if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]: + frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because account {2} + is not linked to warehouse {3} or it is not the default inventory account'''.format( + item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]), + frappe.bold(item.expense_account), frappe.bold(item.warehouse)))) + item.expense_account = warehouse_account[item.warehouse]["account"] else: # check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not @@ -247,10 +253,21 @@ class PurchaseInvoice(BuyingController): (item.purchase_receipt, stock_not_billed_account)) if negative_expense_booked_in_pr: + if for_validate and item.expense_account and item.expense_account != stock_not_billed_account: + frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because + expense is booked against this account in Purchase Receipt {2}'''.format( + item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt)))) + item.expense_account = stock_not_billed_account else: # If no purchase receipt present then book expense in 'Stock Received But Not Billed' # This is done in cases when Purchase Invoice is created before Purchase Receipt + if for_validate and item.expense_account and item.expense_account != stock_not_billed_account: + frappe.msgprint(_('''Row {0}: Expense Head changed to {1} as no Purchase + Receipt is created against Item {2}. This is done to handle accounting for cases + when Purchase Receipt is created after Purchase Invoice'''.format( + item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code)))) + item.expense_account = stock_not_billed_account elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category): @@ -570,6 +587,20 @@ class PurchaseInvoice(BuyingController): else: amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) + auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items')) + + if auto_accounting_for_non_stock_items: + service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") + + if item.purchase_receipt: + # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt + expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0, + 'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail, + 'account':service_received_but_not_billed_account}, ['name']) + + if expense_booked_in_pr: + expense_account = service_received_but_not_billed_account + gl_entries.append(self.get_gl_dict({ "account": expense_account, "against": self.supplier, @@ -1020,7 +1051,7 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() - + def set_status(self, update=False, status=None, update_modified=True): if self.is_new(): if self.get('amended_from'): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index 800ed921bd..86c2e408c0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -16,7 +16,7 @@ frappe.listview_settings['Purchase Invoice'] = { } else if(frappe.datetime.get_diff(doc.due_date) < 0) { return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"]; } else { - return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"]; + return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"]; } } else if(cint(doc.is_return)) { return [__("Return"), "darkgrey", "is_return,=,Yes"]; @@ -24,4 +24,4 @@ frappe.listview_settings['Purchase Invoice'] = { return [__("Paid"), "green", "outstanding_amount,=,0"]; } } -}; +}; \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 6170005061..b5955ca258 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -7,14 +7,15 @@ import unittest import frappe, erpnext import frappe.model from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry -from frappe.utils import cint, flt, today, nowdate, add_days +from frappe.utils import cint, flt, today, nowdate, add_days, getdate import frappe.defaults from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ test_records as pr_test_records, make_purchase_receipt, get_taxes from erpnext.controllers.accounts_controller import get_payment_terms from erpnext.exceptions import InvalidCurrency from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction -from erpnext.accounts.doctype.account.test_account import get_inventory_account +from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account +from erpnext.stock.doctype.item.test_item import create_item test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"] test_ignore = ["Serial No"] @@ -866,6 +867,67 @@ class TestPurchaseInvoice(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + def test_deferred_expense_via_journal_entry(self): + deferred_account = create_account(account_name="Deferred Expense", + parent_account="Current Assets - _TC", company="_Test Company") + + acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + acc_settings.book_deferred_entries_via_journal_entry = 1 + acc_settings.submit_journal_entries = 1 + acc_settings.save() + + item = create_item("_Test Item for Deferred Accounting") + item.enable_deferred_expense = 1 + item.deferred_expense_account = deferred_account + item.save() + + pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True) + pi.set_posting_time = 1 + pi.posting_date = '2019-03-15' + pi.items[0].enable_deferred_expense = 1 + pi.items[0].service_start_date = "2019-01-10" + pi.items[0].service_end_date = "2019-03-15" + pi.items[0].deferred_expense_account = deferred_account + pi.save() + pi.submit() + + pda1 = frappe.get_doc(dict( + doctype='Process Deferred Accounting', + posting_date=nowdate(), + start_date="2019-01-01", + end_date="2019-03-31", + type="Expense", + company="_Test Company" + )) + + pda1.insert() + pda1.submit() + + expected_gle = [ + ["_Test Account Cost for Goods Sold - _TC", 0.0, 33.85, "2019-01-31"], + [deferred_account, 33.85, 0.0, "2019-01-31"], + ["_Test Account Cost for Goods Sold - _TC", 0.0, 43.08, "2019-02-28"], + [deferred_account, 43.08, 0.0, "2019-02-28"], + ["_Test Account Cost for Goods Sold - _TC", 0.0, 23.07, "2019-03-15"], + [deferred_account, 23.07, 0.0, "2019-03-15"] + ] + + gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date + from `tabGL Entry` + where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s + order by posting_date asc, account asc""", (pi.items[0].name, pi.posting_date), as_dict=1) + + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_gle[i][0], gle.account) + self.assertEqual(expected_gle[i][1], gle.credit) + self.assertEqual(expected_gle[i][2], gle.debit) + self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) + + acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + acc_settings.book_deferred_entries_via_journal_entry = 0 + acc_settings.submit_journal_entriessubmit_journal_entries = 0 + acc_settings.save() + def unlink_payment_on_cancel_of_invoice(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 05b85dabd4..5e8279bb08 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -582,14 +582,14 @@ class SalesInvoice(SellingController): def validate_item_code(self): for d in self.get('items'): - if not d.item_code: + if not d.item_code and self.is_opening == "No": msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) def validate_warehouse(self): super(SalesInvoice, self).validate_warehouse() for d in self.get_item_list(): - if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): + if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code)) def validate_delivery_note(self): @@ -1450,11 +1450,17 @@ def get_inter_company_details(doc, doctype): parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company}) company = frappe.get_cached_value("Customer", doc.customer, "represents_company") + if not parties: + frappe.throw(_('No Supplier found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company))) + party = get_internal_party(parties, "Supplier", doc) else: parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company}) company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company") + if not parties: + frappe.throw(_('No Customer found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company))) + party = get_internal_party(parties, "Customer", doc) return { @@ -1519,14 +1525,22 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): def update_details(source_doc, target_doc, source_parent): target_doc.inter_company_invoice_reference = source_doc.name if target_doc.doctype in ["Purchase Invoice", "Purchase Order"]: + currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency') target_doc.company = details.get("company") target_doc.supplier = details.get("party") target_doc.buying_price_list = source_doc.selling_price_list + + if currency: + target_doc.currency = currency else: + currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency') target_doc.company = details.get("company") target_doc.customer = details.get("party") target_doc.selling_price_list = source_doc.buying_price_list + if currency: + target_doc.currency = currency + doclist = get_mapped_doc(doctype, source_name, { doctype: { "doctype": target_doctype, diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index c82a249843..311cc12dd8 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1720,8 +1720,6 @@ class TestSalesInvoice(unittest.TestCase): si.save() si.submit() - from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income - pda1 = frappe.get_doc(dict( doctype='Process Deferred Accounting', posting_date=nowdate(), @@ -1745,51 +1743,53 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, si.name, expected_gle, "2019-01-30") - def test_deferred_error_email(self): + def test_fixed_deferred_revenue(self): deferred_account = create_account(account_name="Deferred Revenue", parent_account="Current Liabilities - _TC", company="_Test Company") + acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + acc_settings.book_deferred_entries_based_on = 'Months' + acc_settings.save() + item = create_item("_Test Item for Deferred Accounting") item.enable_deferred_revenue = 1 item.deferred_revenue_account = deferred_account item.no_of_months = 12 item.save() - si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True) + si = create_sales_invoice(item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True) si.items[0].enable_deferred_revenue = 1 - si.items[0].service_start_date = "2019-01-10" - si.items[0].service_end_date = "2019-03-15" + si.items[0].service_start_date = "2019-01-16" + si.items[0].service_end_date = "2019-03-31" si.items[0].deferred_revenue_account = deferred_account si.save() si.submit() - from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income - - acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - acc_settings.acc_frozen_upto = '2019-01-31' - acc_settings.save() - - pda = frappe.get_doc(dict( + pda1 = frappe.get_doc(dict( doctype='Process Deferred Accounting', - posting_date=nowdate(), + posting_date='2019-03-31', start_date="2019-01-01", end_date="2019-03-31", type="Income", company="_Test Company" )) - pda.insert() - pda.submit() + pda1.insert() + pda1.submit() - email = frappe.db.sql(""" select name from `tabEmail Queue` - where message like %(txt)s """, { - 'txt': "%%%s%%" % "Error while processing deferred accounting for {0}".format(pda.name) - }) + expected_gle = [ + [deferred_account, 10000.0, 0.0, "2019-01-31"], + ["Sales - _TC", 0.0, 10000.0, "2019-01-31"], + [deferred_account, 20000.0, 0.0, "2019-02-28"], + ["Sales - _TC", 0.0, 20000.0, "2019-02-28"], + [deferred_account, 20000.0, 0.0, "2019-03-31"], + ["Sales - _TC", 0.0, 20000.0, "2019-03-31"] + ] - self.assertTrue(email) + check_gl_entries(self, si.name, expected_gle, "2019-01-30") - acc_settings.load_from_db() - acc_settings.acc_frozen_upto = None + acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + acc_settings.book_deferred_entries_based_on = 'Days' acc_settings.save() def test_inter_company_transaction(self): diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py index b2638c7827..d32a348741 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py @@ -45,7 +45,9 @@ class ShippingRule(Document): shipping_amount = 0.0 by_value = False - self.validate_countries(doc) + if doc.get_shipping_address(): + # validate country only if there is address + self.validate_countries(doc) if self.calculate_based_on == 'Net Total': value = doc.base_net_total diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 4d43919f62..83d7967f79 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -180,7 +180,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No if company: condition += "and company =%s" % (company) if from_date and to_date: - condition += "and posting_date between %s and %s" % (company, from_date, to_date) + condition += "and posting_date between %s and %s" % (from_date, to_date) ## Appending the same supplier again if length of suppliers list is 1 ## since tuple of single element list contains None, For example ('Test Supplier 1', ) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 528fb4e113..db91b6696e 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -602,10 +602,14 @@ def get_party_shipping_address(doctype, name): else: return '' -def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None): +def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None): cond = "1=1" if posting_date: - cond = "posting_date <= '{0}'".format(posting_date) + if future_payment: + cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date) + else: + cond = "posting_date <= '{0}'".format(posting_date) + if company: cond += "and company = '{0}'".format(company) diff --git a/erpnext/accounts/report/account_balance/test_account_balance.py b/erpnext/accounts/report/account_balance/test_account_balance.py index 5544fc4673..b6ced312d0 100644 --- a/erpnext/accounts/report/account_balance/test_account_balance.py +++ b/erpnext/accounts/report/account_balance/test_account_balance.py @@ -61,7 +61,7 @@ def make_sales_invoice(): debit_to = 'Debtors - _TC2', income_account = 'Sales - _TC2', expense_account = 'Cost of Goods Sold - _TC2', - cost_center = '_Test Company 2 - _TC2') + cost_center = 'Main - _TC2') diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 4e09f99ae3..2aa9618e55 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -135,12 +135,5 @@ frappe.query_reports["Accounts Payable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable', 9); diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js index d5f18b0982..9c6b0639c0 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -104,12 +104,5 @@ frappe.query_reports["Accounts Payable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable Summary', 9); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 6208eb6946..8dc558a611 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -199,12 +199,5 @@ frappe.query_reports["Accounts Receivable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable', 9); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index c6f852a66d..66aa18058b 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -169,9 +169,11 @@ class ReceivablePayableReport(object): def append_subtotal_row(self, party): sub_total_row = self.total_row_map.get(party) - self.data.append(sub_total_row) - self.data.append({}) - self.update_sub_total_row(sub_total_row, 'Total') + + if sub_total_row: + self.data.append(sub_total_row) + self.data.append({}) + self.update_sub_total_row(sub_total_row, 'Total') def get_voucher_balance(self, gle): if self.filters.get("sales_person"): @@ -232,7 +234,8 @@ class ReceivablePayableReport(object): if self.filters.get('group_by_party'): self.append_subtotal_row(self.previous_party) - self.data.append(self.total_row_map.get('Total')) + if self.data: + self.data.append(self.total_row_map.get('Total')) def append_row(self, row): self.allocate_future_payments(row) @@ -559,6 +562,14 @@ class ReceivablePayableReport(object): conditions, values = self.prepare_conditions() order_by = self.get_order_by_condition() + if self.filters.show_future_payments: + values.insert(2, self.filters.report_date) + + date_condition = """AND (posting_date <= %s + OR (against_voucher IS NULL AND DATE(creation) <= %s))""" + else: + date_condition = "AND posting_date <=%s" + if self.filters.get(scrub(self.party_type)): select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" else: @@ -574,9 +585,8 @@ class ReceivablePayableReport(object): docstatus < 2 and party_type=%s and (party is not null and party != '') - and posting_date <= %s - {1} {2}""" - .format(select_fields, conditions, order_by), values, as_dict=True) + {1} {2} {3}""" + .format(select_fields, date_condition, conditions, order_by), values, as_dict=True) def get_sales_invoices_or_customers_based_on_sales_person(self): if self.filters.get("sales_person"): diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index f0274b4472..2ff5b531c5 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -63,7 +63,7 @@ def make_sales_invoice(): debit_to = 'Debtors - _TC2', income_account = 'Sales - _TC2', expense_account = 'Cost of Goods Sold - _TC2', - cost_center = '_Test Company 2 - _TC2', + cost_center = 'Main - _TC2', do_not_save=1) si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30)) @@ -83,14 +83,14 @@ def make_payment(docname): def make_credit_note(docname): create_sales_invoice(company="_Test Company 2", - customer = '_Test Customer 2', - currency = 'EUR', - qty = -1, - warehouse = 'Finished Goods - _TC2', - debit_to = 'Debtors - _TC2', - income_account = 'Sales - _TC2', - expense_account = 'Cost of Goods Sold - _TC2', - cost_center = '_Test Company 2 - _TC2', - is_return = 1, - return_against = docname) + customer = '_Test Customer 2', + currency = 'EUR', + qty = -1, + warehouse = 'Finished Goods - _TC2', + debit_to = 'Debtors - _TC2', + income_account = 'Sales - _TC2', + expense_account = 'Cost of Goods Sold - _TC2', + cost_center = 'Main - _TC2', + is_return = 1, + return_against = docname) diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index b316f108d0..305cddb102 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -111,7 +111,12 @@ frappe.query_reports["Accounts Receivable Summary"] = { "fieldname":"based_on_payment_terms", "label": __("Based On Payment Terms"), "fieldtype": "Check", - } + }, + { + "fieldname":"show_future_payments", + "label": __("Show Future Payments"), + "fieldtype": "Check", + }, ], onload: function(report) { @@ -122,11 +127,4 @@ frappe.query_reports["Accounts Receivable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable Summary', 9); diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index aa6b42e89d..657b3e8f20 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.get_party_total(args) party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, - self.filters.report_date, self.filters.company) or {} + self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {} for party, party_dict in iteritems(self.party_total): if party_dict.outstanding == 0: diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index d7efbad240..5001ad9f12 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -93,7 +93,7 @@ def get_assets(filters): sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period from (SELECT a.asset_category, - ifnull(sum(case when ds.schedule_date < %(from_date)s then + ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then ds.depreciation_amount else 0 @@ -111,13 +111,11 @@ def get_assets(filters): 0 end), 0) as depreciation_amount_during_the_period from `tabAsset` a, `tabDepreciation Schedule` ds - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != '' group by a.asset_category union SELECT a.asset_category, - ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 - and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) - then + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then 0 else a.opening_accumulated_depreciation diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index c4c24c0bab..4a4ad4d71c 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -4,6 +4,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Balance Sheet', 10); + frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 05dc282661..9c9ada871c 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -29,37 +29,60 @@ def execute(filters=None): for dimension in dimensions: dimension_items = cam_map.get(dimension) if dimension_items: - for account, monthwise_data in iteritems(dimension_items): - row = [dimension, account] - totals = [0, 0, 0] - for year in get_fiscal_years(filters): - last_total = 0 - for relevant_months in period_month_ranges: - period_data = [0, 0, 0] - for month in relevant_months: - if monthwise_data.get(year[0]): - month_data = monthwise_data.get(year[0]).get(month, {}) - for i, fieldname in enumerate(["target", "actual", "variance"]): - value = flt(month_data.get(fieldname)) - period_data[i] += value - totals[i] += value - - period_data[0] += last_total - - if filters.get("show_cumulative"): - last_total = period_data[0] - period_data[1] - - period_data[2] = period_data[0] - period_data[1] - row += period_data - totals[2] = totals[0] - totals[1] - if filters["period"] != "Yearly": - row += totals - data.append(row) + data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, 0) + else: + DCC_allocation = frappe.db.sql('''SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(dimension)s + AND parent NOT IN %(dimension)s + GROUP BY parent''',{'dimension':[dimension]}) + if DCC_allocation: + filters['budget_against_filter'] = [DCC_allocation[0][0]] + cam_map = get_dimension_account_month_map(filters) + dimension_items = cam_map.get(DCC_allocation[0][0]) + if dimension_items: + data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1]) chart = get_chart_data(filters, columns, data) return columns, data, None, chart +def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation): + + for account, monthwise_data in iteritems(dimension_items): + row = [dimension, account] + totals = [0, 0, 0] + for year in get_fiscal_years(filters): + last_total = 0 + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + for month in relevant_months: + if monthwise_data.get(year[0]): + month_data = monthwise_data.get(year[0]).get(month, {}) + for i, fieldname in enumerate(["target", "actual", "variance"]): + value = flt(month_data.get(fieldname)) + period_data[i] += value + totals[i] += value + + period_data[0] += last_total + + if DCC_allocation: + period_data[0] = period_data[0]*(DCC_allocation/100) + period_data[1] = period_data[1]*(DCC_allocation/100) + + if(filters.get("show_cumulative")): + last_total = period_data[0] - period_data[1] + + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + if filters["period"] != "Yearly" : + row += totals + data.append(row) + + return data + + def get_columns(filters): columns = [ { @@ -366,7 +389,7 @@ def get_chart_data(filters, columns, data): budget_values[i] += values[index] actual_values[i] += values[index+1] index += 3 - + return { 'data': { 'labels': labels, diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index e5d0c89918..a984bf46b5 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -5,6 +5,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Cash Flow', 10); + // The last item in the array is the definition for Presentation Currency // filter. It won't be used in cash flow for now so we pop it. Please take // of this if you are working here. diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index 38fd5fa278..09479221fb 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -33,7 +33,6 @@ frappe.query_reports["Consolidated Financial Statement"] = { "fieldname":"period_start_date", "label": __("Start Date"), "fieldtype": "Date", - "default": frappe.datetime.nowdate(), "hidden": 1, "reqd": 1 }, @@ -41,7 +40,6 @@ frappe.query_reports["Consolidated Financial Statement"] = { "fieldname":"period_end_date", "label": __("End Date"), "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), "hidden": 1, "reqd": 1 }, @@ -106,5 +104,16 @@ frappe.query_reports["Consolidated Financial Statement"] = { value = $value.wrap("

").parent().html(); } return value; + }, + onload: function() { + let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + period_start_date: fy.year_start_date, + period_end_date: fy.year_end_date + }); + }); } } diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 4a35a66865..533685d703 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -56,9 +56,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_ to_date = add_months(start_date, months_to_add) start_date = to_date - if to_date == get_first_day(to_date): - # if to_date is the first day, get the last day of previous month - to_date = add_days(to_date, -1) + # Subtract one day from to_date, as it may be first day in next fiscal year or month + to_date = add_days(to_date, -1) if to_date <= year_end_date: # the normal case @@ -387,11 +386,43 @@ def set_gl_entries_by_account( key: value }) + distributed_cost_center_query = "" + if filters and filters.get('cost_center'): + distributed_cost_center_query = """ + UNION ALL + SELECT posting_date, + account, + debit*(DCC_allocation.percentage_allocation/100) as debit, + credit*(DCC_allocation.percentage_allocation/100) as credit, + is_opening, + fiscal_year, + debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency, + credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency, + account_currency + FROM `tabGL Entry`, + ( + SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(cost_center)s + AND parent NOT IN %(cost_center)s + AND is_cancelled = 0 + GROUP BY parent + ) as DCC_allocation + WHERE company=%(company)s + {additional_conditions} + AND posting_date <= %(to_date)s + AND cost_center = DCC_allocation.parent + """.format(additional_conditions=additional_conditions.replace("and cost_center in %(cost_center)s ", '')) + gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` where company=%(company)s {additional_conditions} and posting_date <= %(to_date)s - order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec + and is_cancelled = 0 + {distributed_cost_center_query} + order by account, posting_date""".format( + additional_conditions=additional_conditions, + distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec if filters and filters.get('presentation_currency'): convert_to_presentation_currency(gl_entries, get_currency(filters)) @@ -489,4 +520,4 @@ def get_columns(periodicity, period_list, accumulated_values=1, company=None): "width": 150 }) - return columns + return columns \ No newline at end of file diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 2aecd6b717..1fc0f79478 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -164,12 +164,5 @@ frappe.query_reports["General Ledger"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('General Ledger', 15) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index f83a2595f6..fcd36e4e6e 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -128,18 +128,53 @@ def get_gl_entries(filters): filters['company_fb'] = frappe.db.get_value("Company", filters.get("company"), 'default_finance_book') + distributed_cost_center_query = "" + if filters and filters.get('cost_center'): + select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency, + credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """ + + distributed_cost_center_query = """ + UNION ALL + SELECT name as gl_entry, + posting_date, + account, + party_type, + party, + voucher_type, + voucher_no, + cost_center, project, + against_voucher_type, + against_voucher, + account_currency, + remarks, against, + is_opening, `tabGL Entry`.creation {select_fields_with_percentage} + FROM `tabGL Entry`, + ( + SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(cost_center)s + AND parent NOT IN %(cost_center)s + GROUP BY parent + ) as DCC_allocation + WHERE company=%(company)s + {conditions} + AND posting_date <= %(to_date)s + AND cost_center = DCC_allocation.parent + """.format(select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", '')) + gl_entries = frappe.db.sql( """ select name as gl_entry, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center, project, against_voucher_type, against_voucher, account_currency, - remarks, against, is_opening {select_fields} + remarks, against, is_opening, creation {select_fields} from `tabGL Entry` where company=%(company)s {conditions} + {distributed_cost_center_query} {order_by_statement} """.format( - select_fields=select_fields, conditions=get_conditions(filters), + select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query, order_by_statement=order_by_statement ), filters, as_dict=1) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js index f88906a0f1..b709ab9b57 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js @@ -4,11 +4,18 @@ frappe.query_reports["Item-wise Purchase Register"] = { "filters": [ { - "fieldname":"date_range", - "label": __("Date Range"), - "fieldtype": "DateRange", - "default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], - "reqd": 1 + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + "reqd": 1, + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1, }, { "fieldname": "item_code", diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 1f78c7a006..3445df7206 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -14,7 +14,6 @@ def execute(filters=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): if not filters: filters = {} - filters.update({"from_date": filters.get("date_range")[0], "to_date": filters.get("date_range")[1]}) columns = get_columns(additional_table_columns, filters) company_currency = erpnext.get_company_currency(filters.company) @@ -266,13 +265,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index 8a9c76f26f..39fb3ca5ee 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -4,11 +4,18 @@ frappe.query_reports["Item-wise Sales Register"] = { "filters": [ { - "fieldname": "date_range", - "label": __("Date Range"), - "fieldtype": "DateRange", - "default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], - "reqd": 1 + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + "reqd": 1, + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1, }, { "fieldname": "customer", diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 92a22e62f1..a05dcd75ce 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -14,7 +14,6 @@ def execute(filters=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): if not filters: filters = {} - filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]}) columns = get_columns(additional_table_columns, filters) company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") @@ -224,7 +223,7 @@ def get_columns(additional_table_columns, filters): } ] - if filters.get('group_by') != 'Terriotory': + if filters.get('group_by') != 'Territory': columns.extend([ { 'label': _("Territory"), @@ -305,13 +304,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] @@ -537,6 +529,13 @@ def get_tax_accounts(item_list, columns, company_currency, 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 + }, + { + 'fieldname': 'currency', + 'label': _('Currency'), + 'fieldtype': 'Currency', + 'width': 80, + 'hidden': 1 } ] diff --git a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.js b/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.js deleted file mode 100644 index 6e13d67766..0000000000 --- a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.query_reports["Ordered Items To Be Billed"] = { - "filters": [ - - ] -} diff --git a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.json b/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.json deleted file mode 100644 index c983dc9629..0000000000 --- a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "add_total_row": 1, - "apply_user_permissions": 1, - "creation": "2013-02-21 14:26:44", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-11-06 13:04:51.559061", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Ordered Items To Be Billed", - "owner": "Administrator", - "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name:150\",\n`tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.base_amount as \"Amount:Currency:110\",\n (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (`tabSales Order Item`.base_amount - (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order`.`company` as \"Company:Link/Company:\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Closed\"\n and `tabSales Order Item`.amount > 0\n and `tabSales Order Item`.billed_amt < `tabSales Order Item`.amount\norder by `tabSales Order`.transaction_date asc", - "ref_doctype": "Sales Invoice", - "report_name": "Ordered Items To Be Billed", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts Manager" - }, - { - "role": "Accounts User" - } - ] -} \ No newline at end of file diff --git a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.py b/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.py deleted file mode 100644 index ec0d2f39f3..0000000000 --- a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data - -def execute(filters=None): - columns = get_column() - args = get_args() - data = get_ordered_to_be_billed_data(args) - return columns, data - -def get_column(): - return [ - _("Sales Order") + ":Link/Sales Order:120", _("Status") + "::120", _("Date") + ":Date:100", - _("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120", - _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", - _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100", - _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120", - ] - -def get_args(): - return {'doctype': 'Sales Order', 'party': 'customer', - 'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'} \ No newline at end of file diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 2b946c0540..1c461efbcd 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -6,6 +6,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Profit and Loss Statement', 10); + frappe.query_reports["Profit and Loss Statement"]["filters"].push( { "fieldname": "project", diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py index 6e9b31f2f6..60e675f2f1 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py @@ -105,6 +105,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name): def prepare_data(accounts, filters, total_row, parent_children_map, based_on): data = [] + new_accounts = accounts company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") for d in accounts: @@ -118,6 +119,19 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on): "currency": company_currency, "based_on": based_on } + if based_on == 'cost_center': + cost_center_doc = frappe.get_doc("Cost Center",d.name) + if not cost_center_doc.enable_distributed_cost_center: + DCC_allocation = frappe.db.sql("""SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(cost_center)s + AND parent NOT IN %(cost_center)s + GROUP BY parent""",{'cost_center': [d.name]}) + if DCC_allocation: + for account in new_accounts: + if account['name'] == DCC_allocation[0][0]: + for value in value_fields: + d[value] += account[value]*(DCC_allocation[0][1]/100) for key in value_fields: row[key] = flt(d.get(key, 0.0), 3) diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index b2b95b2b81..f34ea57163 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -56,11 +56,4 @@ frappe.query_reports["Purchase Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); \ No newline at end of file +erpnext.utils.add_dimensions('Purchase Register', 7); \ No newline at end of file diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 9dee656d4a..85bbceab82 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -68,12 +68,5 @@ frappe.query_reports["Sales Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Sales Register', 7); diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index 4ac0f65611..a9fb237a04 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -111,7 +111,7 @@ def get_gle_map(filters): # {"purchase_invoice": list of dict of all gle created for this invoice} gle_map = {} gle = frappe.db.get_all('GL Entry',\ - {"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]]}, + {"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]], 'is_cancelled': 0}, ["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"]) for d in gle: diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 07752e1e62..9c0854c5d3 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -102,14 +102,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "initial_depth": 3 } - erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); + erpnext.utils.add_dimensions('Trial Balance', 6); }); diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5165495786..16146f4480 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -57,6 +57,9 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb frappe.cache().hset("fiscal_years", company, fiscal_years) + if not transaction_date and not fiscal_year: + return fiscal_years + if transaction_date: transaction_date = getdate(transaction_date) @@ -79,6 +82,23 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb if verbose==1: frappe.msgprint(error_msg) raise FiscalYearError(error_msg) +@frappe.whitelist() +def get_fiscal_year_filter_field(company=None): + field = { + "fieldtype": "Select", + "options": [], + "operator": "Between", + "query_value": True + } + fiscal_years = get_fiscal_years(company=company) + for fiscal_year in fiscal_years: + field["options"].append({ + "label": fiscal_year.name, + "value": fiscal_year.name, + "query_value": [fiscal_year.year_start_date.strftime("%Y-%m-%d"), fiscal_year.year_end_date.strftime("%Y-%m-%d")] + }) + return field + def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None): years = [f[0] for f in get_fiscal_years(date, label=_(label), company=company)] if fiscal_year not in years: @@ -817,7 +837,7 @@ def create_payment_gateway_account(gateway): pass @frappe.whitelist() -def update_cost_center(docname, cost_center_name, cost_center_number, company): +def update_cost_center(docname, cost_center_name, cost_center_number, company, merge): ''' Renames the document by adding the number as a prefix to the current name and updates all transaction where it was present. @@ -833,7 +853,7 @@ def update_cost_center(docname, cost_center_name, cost_center_number, company): new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company) if docname != new_name: - frappe.rename_doc("Cost Center", docname, new_name, force=1) + frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge) return new_name def validate_field_number(doctype_name, docname, number_value, company, field_name): @@ -942,4 +962,4 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date): tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1): gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d) - return gl_entries \ No newline at end of file + return gl_entries diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 9af45d16b6..7f3c1de406 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -5,14 +5,23 @@ import frappe import json from frappe.utils import nowdate, add_months, get_date_str from frappe import _ -from erpnext.accounts.utils import get_fiscal_year - +from erpnext.accounts.dashboard_fixtures import _get_fiscal_year +from erpnext.buying.dashboard_fixtures import get_company_for_dashboards def get_data(): + + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + + year_start_date = get_date_str(fiscal_year.get('year_start_date')) + year_end_date = get_date_str(fiscal_year.get('year_end_date')) + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts(fiscal_year, year_start_date, year_end_date), + "number_cards": get_number_cards(fiscal_year, year_start_date, year_end_date), }) def get_dashboards(): @@ -31,12 +40,7 @@ def get_dashboards(): ] }] -fiscal_year = get_fiscal_year(date=nowdate()) -year_start_date = get_date_str(fiscal_year[1]) -year_end_date = get_date_str(fiscal_year[2]) - - -def get_charts(): +def get_charts(fiscal_year, year_start_date, year_end_date): company = get_company_for_dashboards() return [ { @@ -55,8 +59,8 @@ def get_charts(): "company": company, "status": "In Location", "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year[0], - "to_fiscal_year": fiscal_year[0], + "from_fiscal_year": fiscal_year.get('name'), + "to_fiscal_year": fiscal_year.get('name'), "period_start_date": year_start_date, "period_end_date": year_end_date, "date_based_on": "Purchase Date", @@ -134,7 +138,7 @@ def get_charts(): } ] -def get_number_cards(): +def get_number_cards(fiscal_year, year_start_date, year_end_date): return [ { "name": "Total Assets", @@ -172,14 +176,4 @@ def get_number_cards(): "filters_json": "[]", "doctype": "Number Card" } - ] - -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None \ No newline at end of file + ] \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 94939fdd2a..449a5facb0 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -58,7 +58,7 @@ "type": "Report" }, { - "label": "Assets Dashboard", + "label": "Dashboard", "link_to": "Asset", "type": "Dashboard" } diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index efb2de3f26..c0c2566fe2 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -1,559 +1,140 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:asset_name", - "beta": 0, - "creation": "2017-10-19 16:50:22.879545", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:asset_name", + "creation": "2017-10-19 16:50:22.879545", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_name", + "asset_category", + "company", + "column_break_3", + "item_code", + "item_name", + "section_break_6", + "maintenance_team", + "column_break_9", + "maintenance_manager", + "maintenance_manager_name", + "section_break_8", + "asset_maintenance_tasks" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Asset Name", - "length": 0, - "no_copy": 0, - "options": "Asset", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "asset_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset Name", + "options": "Asset", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.asset_category", - "fieldname": "asset_category", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Asset Category", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "asset_category", + "fieldtype": "Read Only", + "label": "Asset Category" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_serial_no", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Select Serial No", - "length": 0, - "no_copy": 0, - "options": "Serial No", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "serial_no", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Serial No", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_team", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Maintenance Team", + "options": "Asset Maintenance Team", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_team", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maintenance Team", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Team", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager", - "fieldname": "maintenance_manager", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Manager", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_manager", + "fieldtype": "Data", + "hidden": 1, + "label": "Maintenance Manager", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager_name", - "fieldname": "maintenance_manager_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Manager Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_manager_name", + "fieldtype": "Read Only", + "label": "Maintenance Manager Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tasks", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Tasks" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance_tasks", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Tasks", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Task", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "asset_maintenance_tasks", + "fieldtype": "Table", + "label": "Maintenance Tasks", + "options": "Asset Maintenance Task", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-22 17:20:54.711885", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-28 20:28:32.993823", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Quality Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index 3f046113a0..1869a29c8d 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -38,10 +38,10 @@ class AssetMaintenance(Document): @frappe.whitelist() def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date): - team_member = frappe.get_doc('User', assign_to_member).email + team_member = frappe.db.get_value('User', assign_to_member, "email") args = { 'doctype' : 'Asset Maintenance', - 'assign_to' : team_member, + 'assign_to' : [team_member], 'name' : asset_maintenance_name, 'description' : maintenance_task, 'date' : next_due_date @@ -77,7 +77,7 @@ def calculate_next_due_date(periodicity, start_date = None, end_date = None, las def update_maintenance_log(asset_maintenance, item_code, item_name, task): asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance, - "task": task.maintenance_task, "maintenance_status": ('in',['Planned','Overdue'])}) + "task": task.name, "maintenance_status": ('in',['Planned','Overdue'])}) if not asset_maintenance_log: asset_maintenance_log = frappe.get_doc({ @@ -86,7 +86,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): "asset_name": asset_maintenance, "item_code": item_code, "item_name": item_name, - "task": task.maintenance_task, + "task": task.name, "has_certificate": task.certificate_required, "description": task.description, "assign_to_name": task.assign_to_name, diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json index 42aecdf44a..7395bec1e6 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json @@ -1,819 +1,210 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2017-10-23 16:58:44.424309", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2017-10-23 16:58:44.424309", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_maintenance", + "naming_series", + "asset_name", + "column_break_2", + "item_code", + "item_name", + "section_break_5", + "task", + "task_name", + "maintenance_type", + "periodicity", + "assign_to_name", + "column_break_6", + "due_date", + "completion_date", + "maintenance_status", + "section_break_12", + "has_certificate", + "certificate_attachement", + "section_break_6", + "description", + "column_break_9", + "actions_performed", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Asset Maintenance", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "asset_maintenance", + "fieldtype": "Link", + "label": "Asset Maintenance", + "options": "Asset Maintenance" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "ACC-AML-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "ACC-AML-.YYYY.-", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.asset_name", - "fieldname": "asset_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Asset Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "asset_maintenance.asset_name", + "fieldname": "asset_name", + "fieldtype": "Read Only", + "label": "Asset Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "asset_maintenance.item_code", + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "asset_maintenance.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "task", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Task", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Task", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "task", + "fieldtype": "Link", + "label": "Task", + "options": "Asset Maintenance Task" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.maintenance_type", - "fieldname": "maintenance_type", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Type", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.maintenance_type", + "fieldname": "maintenance_type", + "fieldtype": "Read Only", + "label": "Maintenance Type" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.periodicity", - "fieldname": "periodicity", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Periodicity", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.periodicity", + "fieldname": "periodicity", + "fieldtype": "Data", + "label": "Periodicity", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.assign_to_name", - "fieldname": "assign_to_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Assign To", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.assign_to_name", + "fieldname": "assign_to_name", + "fieldtype": "Read Only", + "label": "Assign To" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.next_due_date", - "fieldname": "due_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Due Date", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.next_due_date", + "fieldname": "due_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Due Date", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "completion_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Completion Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "completion_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Completion Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Maintenance Status", - "length": 0, - "no_copy": 0, - "options": "Planned\nCompleted\nCancelled\nOverdue", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Maintenance Status", + "options": "Planned\nCompleted\nCancelled\nOverdue", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_12", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_12", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.certificate_required", - "fieldname": "has_certificate", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Has Certificate ", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fetch_from": "task.certificate_required", + "fieldname": "has_certificate", + "fieldtype": "Check", + "label": "Has Certificate " + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.has_certificate", - "fieldname": "certificate_attachement", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Certificate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.has_certificate", + "fieldname": "certificate_attachement", + "fieldtype": "Attach", + "label": "Certificate" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.description", - "fieldname": "description", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.description", + "fieldname": "description", + "fieldtype": "Read Only", + "label": "Description", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "actions_performed", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Actions performed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "actions_performed", + "fieldtype": "Text Editor", + "label": "Actions performed" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Asset Maintenance Log", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Maintenance Log", + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "task.maintenance_task", + "fieldname": "task_name", + "fieldtype": "Data", + "in_preview": 1, + "label": "Task Name", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:51.457835", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance Log", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-05-28 20:51:48.238397", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance Log", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py index d98cc310b3..2a5666d506 100644 --- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py +++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py @@ -7,5 +7,4 @@ import frappe from frappe.model.document import Document class AssetMaintenanceTask(Document): - def autoname(self): - self.name = self.maintenance_task + pass diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 186bfb23af..c6e2ffa634 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -5,13 +5,24 @@ import frappe import json from frappe import _ from frappe.utils import nowdate -from erpnext.accounts.utils import get_fiscal_year +from erpnext.accounts.dashboard_fixtures import _get_fiscal_year def get_data(): + + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + + company = frappe.get_doc("Company", get_company_for_dashboards()) + fiscal_year_name = fiscal_year.get("name") + start_date = str(fiscal_year.get("year_start_date")) + end_date = str(fiscal_year.get("year_end_date")) + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts(company, fiscal_year_name, start_date, end_date), + "number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date), }) def get_company_for_dashboards(): @@ -24,12 +35,6 @@ def get_company_for_dashboards(): return company_list[0].name return None -company = frappe.get_doc("Company", get_company_for_dashboards()) -fiscal_year = get_fiscal_year(nowdate(), as_dict=1) -fiscal_year_name = fiscal_year.get("name") -start_date = str(fiscal_year.get("year_start_date")) -end_date = str(fiscal_year.get("year_end_date")) - def get_dashboards(): return [{ "name": "Buying", @@ -48,7 +53,7 @@ def get_dashboards(): ] }] -def get_charts(): +def get_charts(company, fiscal_year_name, start_date, end_date): return [ { "name": "Purchase Order Analysis", @@ -139,7 +144,7 @@ def get_charts(): } ] -def get_number_cards(): +def get_number_cards(company, fiscal_year_name, start_date, end_date): return [ { "name": "Annual Purchase", @@ -150,8 +155,7 @@ def get_number_cards(): ["Purchase Order", "transaction_date", "Between", [start_date, end_date], False], ["Purchase Order", "status", "not in", ["Draft", "Cancelled", "Closed", None], False], ["Purchase Order", "docstatus", "=", 1, False], - ["Purchase Order", "company", "=", company.name, False], - ["Purchase Order", "transaction_date", "Between", [start_date,end_date], False] + ["Purchase Order", "company", "=", company.name, False] ]), "function": "Sum", "is_public": 1, diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index c4cdce1615..565d39c3c8 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -33,7 +33,12 @@ { "hidden": 0, "label": "Other Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + }, + { + "hidden": 0, + "label": "Regional", + "links": "[\n {\n \"description\": \"Import Italian Purchase Invoices\",\n \"label\": \"Import Supplier Invoice\",\n \"name\": \"Import Supplier Invoice\",\n \"type\": \"doctype\"\n } \n]" } ], "cards_label": "", @@ -55,7 +60,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-28 13:32:49.960574", + "modified": "2020-06-29 19:30:24.983050", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index 01b40cd26f..e496e9628d 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -11,21 +11,21 @@ frappe.tour['Buying Settings'] = [ { fieldname: "supp_master_name", title: "Supplier Naming By", - description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "Naming Series" + __(" choose the 'Naming Series' option."), + description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ") + "Naming Series" + __(" choose the 'Naming Series' option."), }, { fieldname: "buying_price_list", title: "Default Buying Price List", - description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List.") + description: __("Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List.") }, { fieldname: "po_required", title: "Purchase Order Required for Purchase Invoice & Receipt Creation", - description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in supplier master.") + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.") }, { fieldname: "pr_required", title: "Purchase Receipt Required for Purchase Invoice Creation", - description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in supplier master.") + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.") } ]; \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index a4f60fbba5..13f5cb0c81 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -38,6 +38,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -135,7 +137,9 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -145,7 +149,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -157,7 +163,9 @@ "options": "PUR-ORD-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -170,14 +178,18 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", "description": "Fetch items based on Default Supplier.", "fieldname": "get_items_from_open_material_requests", "fieldtype": "Button", - "label": "Get Items from Open Material Requests" + "label": "Get Items from Open Material Requests", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -186,7 +198,9 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -198,13 +212,17 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -216,27 +234,35 @@ "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "schedule_date", "fieldtype": "Date", - "label": "Required By" + "label": "Required By", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.docstatus===1", "fieldname": "order_confirmation_no", "fieldtype": "Data", - "label": "Order Confirmation No" + "label": "Order Confirmation No", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.order_confirmation_no", "fieldname": "order_confirmation_date", "fieldtype": "Date", - "label": "Order Confirmation Date" + "label": "Order Confirmation Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -248,19 +274,25 @@ "oldfieldtype": "Data", "options": "Purchase Order", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "drop_ship", "fieldtype": "Section Break", - "label": "Drop Ship" + "label": "Drop Ship", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer", "fieldtype": "Link", "label": "Customer", "options": "Customer", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -268,31 +300,41 @@ "fieldtype": "Data", "label": "Customer Name", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_19", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_person", "fieldtype": "Link", "label": "Customer Contact", - "options": "Contact" + "options": "Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_display", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_mobile", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Mobile No", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_email", @@ -300,46 +342,60 @@ "hidden": 1, "label": "Customer Contact Email", "options": "Email", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", @@ -347,32 +403,42 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -382,7 +448,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -392,18 +460,24 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_price_list", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -411,14 +485,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -427,11 +505,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Warehouse' in each row of the Items table.", @@ -439,11 +521,15 @@ "fieldtype": "Link", "label": "Set Target Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -452,25 +538,33 @@ "in_standard_filter": 1, "label": "Supply Raw Materials", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", "fieldname": "supplier_warehouse", "fieldtype": "Link", "label": "Supplier Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -480,26 +574,34 @@ "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_break_48", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Purchase Order Pricing Rule", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_material_details", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", @@ -509,17 +611,23 @@ "oldfieldtype": "Table", "options": "Purchase Order Item Supplied", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sb_last_purchase", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -527,7 +635,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -538,18 +648,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_26", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -559,20 +675,26 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -581,22 +703,30 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_52", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -604,13 +734,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -619,13 +753,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -635,7 +773,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -645,7 +785,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -656,11 +798,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_39", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -670,7 +816,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -680,7 +828,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -688,14 +838,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "discount_section", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -703,7 +857,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -711,28 +867,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_45", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -743,7 +909,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -752,7 +920,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "In Words will be visible once you save the Purchase Order.", @@ -762,7 +932,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounded_total", @@ -772,12 +944,16 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break4", "fieldtype": "Column Break", - "oldfieldtype": "Column Break" + "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "grand_total", @@ -787,7 +963,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -796,20 +974,26 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounded_total", "fieldtype": "Currency", "label": "Rounded Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -818,7 +1002,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advance_paid", @@ -827,19 +1013,25 @@ "no_copy": 1, "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -847,7 +1039,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -856,7 +1050,9 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -865,21 +1061,27 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", "label": "More Information", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -894,7 +1096,9 @@ "print_hide": 1, "read_only": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "ref_sq", @@ -905,7 +1109,9 @@ "oldfieldname": "ref_sq", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -915,18 +1121,24 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_order_reference", "fieldtype": "Link", "label": "Inter Company Order Reference", "options": "Sales Order", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_74", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -936,7 +1148,9 @@ "label": "% Received", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -946,7 +1160,9 @@ "label": "% Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -956,6 +1172,8 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -966,7 +1184,9 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -978,11 +1198,15 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_86", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -990,19 +1214,25 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section" + "label": "Subscription Section", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1010,7 +1240,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1018,11 +1250,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1031,44 +1267,72 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", - "options": "Tax Category" + "options": "Tax Category", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "supplied_items", "fieldname": "set_reserve_warehouse", "fieldtype": "Link", "label": "Set Reserve Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "tracking_section", "fieldtype": "Section Break", - "label": "Tracking" + "label": "Tracking", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_75", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-24 12:13:14.186280", + "modified": "2020-06-13 22:25:47.333850", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", @@ -1112,6 +1376,12 @@ "read": 1, "role": "Purchase Manager", "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User" } ], "search_fields": "status, transaction_date, supplier,grand_total", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index f62df20ae1..c7efb8a1a1 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -71,6 +71,15 @@ class PurchaseOrder(BuyingController): "compare_fields": [["project", "="], ["item_code", "="], ["uom", "="], ["conversion_factor", "="]], "is_child_table": True + }, + "Material Request": { + "ref_dn_field": "material_request", + "compare_fields": [["company", "="]], + }, + "Material Request Item": { + "ref_dn_field": "material_request_item", + "compare_fields": [["project", "="], ["item_code", "="]], + "is_child_table": True } }) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 1712369e60..813286f7fa 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -118,7 +118,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.get("items")[0].amount, 1400) self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3) - + def test_add_new_item_in_update_child_qty_rate(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 @@ -144,7 +144,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 2) self.assertEqual(po.status, 'To Receive and Bill') - + def test_remove_item_in_update_child_qty_rate(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 @@ -185,6 +185,23 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 1) self.assertEqual(po.status, 'To Receive and Bill') + def test_update_child_qty_rate_perm(self): + po = create_purchase_order(item_code= "_Test Item", qty=4) + + user = 'test@example.com' + test_user = frappe.get_doc('User', user) + test_user.add_roles("Accounts User") + frappe.set_user(user) + + # update qty + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + + # add new item + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + frappe.set_user("Administrator") + def test_update_qty(self): po = create_purchase_order() @@ -689,7 +706,7 @@ class TestPurchaseOrder(unittest.TestCase): po.save() self.assertEqual(po.schedule_date, add_days(nowdate(), 2)) - + def test_po_optional_blanket_order(self): """ Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1. diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 95db33b0f8..dfdb487f9e 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -25,6 +25,7 @@ class RequestforQuotation(BuyingController): self.validate_duplicate_supplier() self.validate_supplier_list() validate_for_items(self) + super(RequestforQuotation, self).set_qty_as_per_stock_uom() self.update_email_id() def validate_duplicate_supplier(self): @@ -278,6 +279,7 @@ def create_rfq_items(sq_doc, supplier, data): "description": data.description, "qty": data.qty, "rate": data.rate, + "conversion_factor": data.conversion_factor if data.conversion_factor else None, "supplier_part_no": frappe.db.get_value("Item Supplier", {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no"), "warehouse": data.warehouse or '', "request_for_quotation_item": data.name, diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py index dbd9f02278..3de9526c4f 100644 --- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py @@ -6,12 +6,14 @@ from __future__ import unicode_literals import unittest import frappe -from erpnext.templates.pages.rfq import check_supplier_has_docname_access from frappe.utils import nowdate +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.templates.pages.rfq import check_supplier_has_docname_access +from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation +from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation class TestRequestforQuotation(unittest.TestCase): def test_quote_status(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation rfq = make_request_for_quotation() self.assertEqual(rfq.get('suppliers')[0].quote_status, 'Pending') @@ -31,7 +33,6 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(rfq.get('suppliers')[1].quote_status, 'No Quote') def test_make_supplier_quotation(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation rfq = make_request_for_quotation() sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier) @@ -51,15 +52,13 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(sq1.get('items')[0].qty, 5) def test_make_supplier_quotation_with_special_characters(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation - frappe.delete_doc_if_exists("Supplier", "_Test Supplier '1", force=1) supplier = frappe.new_doc("Supplier") supplier.supplier_name = "_Test Supplier '1" supplier.supplier_group = "_Test Supplier Group" supplier.insert() - rfq = make_request_for_quotation(supplier_wt_appos) + rfq = make_request_for_quotation(supplier_data=supplier_wt_appos) sq = make_supplier_quotation(rfq.name, supplier_wt_appos[0].get("supplier")) sq.submit() @@ -76,7 +75,6 @@ class TestRequestforQuotation(unittest.TestCase): frappe.form_dict.name = None def test_make_supplier_quotation_from_portal(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation rfq = make_request_for_quotation() rfq.get('items')[0].rate = 100 rfq.supplier = rfq.suppliers[0].supplier @@ -90,12 +88,34 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(supplier_quotation_doc.get('items')[0].qty, 5) self.assertEqual(supplier_quotation_doc.get('items')[0].amount, 500) + def test_make_multi_uom_supplier_quotation(self): + item_code = "_Test Multi UOM RFQ Item" + if not frappe.db.exists('Item', item_code): + item = make_item(item_code, {'stock_uom': '_Test UOM'}) + row = item.append('uoms', { + 'uom': 'Kg', + 'conversion_factor': 2 + }) + row.db_update() -def make_request_for_quotation(supplier_data=None): + rfq = make_request_for_quotation(item_code="_Test Multi UOM RFQ Item", uom="Kg", conversion_factor=2) + rfq.get('items')[0].rate = 100 + rfq.supplier = rfq.suppliers[0].supplier + + self.assertEqual(rfq.items[0].stock_qty, 10) + + supplier_quotation_name = create_supplier_quotation(rfq) + supplier_quotation = frappe.get_doc('Supplier Quotation', supplier_quotation_name) + + self.assertEqual(supplier_quotation.items[0].qty, 5) + self.assertEqual(supplier_quotation.items[0].stock_qty, 10) + +def make_request_for_quotation(**args): """ :param supplier_data: List containing supplier data """ - supplier_data = supplier_data if supplier_data else get_supplier_data() + args = frappe._dict(args) + supplier_data = args.get("supplier_data") if args.get("supplier_data") else get_supplier_data() rfq = frappe.new_doc('Request for Quotation') rfq.transaction_date = nowdate() rfq.status = 'Draft' @@ -106,11 +126,13 @@ def make_request_for_quotation(supplier_data=None): rfq.append('suppliers', data) rfq.append("items", { - "item_code": "_Test Item", + "item_code": args.item_code or "_Test Item", "description": "_Test Item", - "uom": "_Test UOM", - "qty": 5, - "warehouse": "_Test Warehouse - _TC", + "uom": args.uom or "_Test UOM", + "stock_uom": args.stock_uom or "_Test UOM", + "qty": args.qty or 5, + "conversion_factor": args.conversion_factor or 1.0, + "warehouse": args.warehouse or "_Test Warehouse - _TC", "schedule_date": nowdate() }) diff --git a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json index 0159df962e..408f49f523 100644 --- a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json +++ b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2016-02-25 08:04:02.452958", "doctype": "DocType", @@ -9,6 +10,7 @@ "supplier_part_no", "column_break_3", "item_name", + "schedule_date", "section_break_5", "description", "item_group", @@ -18,9 +20,11 @@ "image_view", "quantity", "qty", + "stock_uom", "col_break2", - "schedule_date", "uom", + "conversion_factor", + "stock_qty", "warehouse_and_reference", "warehouse", "project_name", @@ -33,7 +37,7 @@ "fields": [ { "bold": 1, - "columns": 3, + "columns": 2, "fieldname": "item_code", "fieldtype": "Link", "in_list_view": 1, @@ -98,7 +102,7 @@ { "fieldname": "quantity", "fieldtype": "Section Break", - "label": "Quantity" + "label": "Quantity & Stock" }, { "bold": 1, @@ -129,12 +133,12 @@ { "fieldname": "uom", "fieldtype": "Link", + "in_list_view": 1, "label": "UOM", "oldfieldname": "uom", "oldfieldtype": "Link", "options": "UOM", "print_width": "100px", - "read_only": 1, "reqd": 1, "width": "100px" }, @@ -144,7 +148,7 @@ "label": "Warehouse and Reference" }, { - "columns": 3, + "columns": 2, "fieldname": "warehouse", "fieldtype": "Link", "in_list_view": 1, @@ -202,6 +206,7 @@ }, { "allow_on_submit": 1, + "default": "0", "fieldname": "page_break", "fieldtype": "Check", "label": "Page Break", @@ -219,10 +224,36 @@ { "fieldname": "section_break_23", "fieldtype": "Section Break" + }, + { + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "conversion_factor", + "fieldtype": "Float", + "label": "UOM Conversion Factor", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "stock_qty", + "fieldtype": "Float", + "label": "Qty as per Stock UOM", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "istable": 1, - "modified": "2019-05-01 17:50:23.703801", + "links": [], + "modified": "2020-06-12 19:10:36.333441", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation Item", diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index 8fe2f388b0..6e4bbc95a2 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-27 17:17:52.075947", + "modified": "2020-06-01 12:55:09.234944", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json index a788ccd4cc..6d765af137 100644 --- a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -1,19 +1,19 @@ { - "action": "Update Settings", + "action": "Show Form Tour", "creation": "2020-05-06 15:53:44.667414", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, + "is_mandatory": 1, + "is_single": 1, "is_skipped": 0, - "modified": "2020-05-12 18:30:06.323797", + "modified": "2020-06-01 12:52:57.668870", "modified_by": "Administrator", "name": "Buying Settings", "owner": "Administrator", "reference_document": "Buying Settings", "show_full_form": 0, "title": "Configure Buying Settings.", - "validate_action": 1 + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 39668795cb..88a865f0f8 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import flt def execute(filters=None): columns = get_columns(filters) @@ -54,15 +55,16 @@ def get_columns(filters): "width": 140 }, { - "label": _("Description"), - "fieldname": "description", - "fieldtype": "Data", - "width": 200 + "label": _("Item"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 150 }, { "label": _("Quantity"), "fieldname": "quantity", - "fieldtype": "Int", + "fieldtype": "Float", "width": 140 }, { @@ -118,7 +120,7 @@ def get_columns(filters): }, { "label": _("Purchase Order Amount(Company Currency)"), - "fieldname": "purchase_order_amt_usd", + "fieldname": "purchase_order_amt_in_company_currency", "fieldtype": "Float", "width": 140 }, @@ -175,17 +177,17 @@ def get_data(filters): "requesting_site": po.warehouse, "requestor": po.owner, "material_request_no": po.material_request, - "description": po.description, - "quantity": po.qty, + "item_code": po.item_code, + "quantity": flt(po.qty), "unit_of_measurement": po.stock_uom, "status": po.status, "purchase_order_date": po.transaction_date, "purchase_order": po.parent, "supplier": po.supplier, - "estimated_cost": mr_record.get('amount'), - "actual_cost": pi_records.get(po.name), - "purchase_order_amt": po.amount, - "purchase_order_amt_in_company_currency": po.base_amount, + "estimated_cost": flt(mr_record.get('amount')), + "actual_cost": flt(pi_records.get(po.name)), + "purchase_order_amt": flt(po.amount), + "purchase_order_amt_in_company_currency": flt(po.base_amount), "expected_delivery_date": po.schedule_date, "actual_delivery_date": pr_records.get(po.name) } @@ -198,9 +200,14 @@ def get_mapped_mr_details(conditions): SELECT par.transaction_date, par.per_ordered, + par.owner, child.name, child.parent, - child.amount + child.amount, + child.qty, + child.item_code, + child.uom, + par.status FROM `tabMaterial Request` par, `tabMaterial Request Item` child WHERE par.per_ordered>=0 @@ -217,7 +224,15 @@ def get_mapped_mr_details(conditions): procurement_record_details = dict( material_request_date=record.transaction_date, material_request_no=record.parent, - estimated_cost=record.amount + requestor=record.owner, + item_code=record.item_code, + estimated_cost=flt(record.amount), + quantity=flt(record.qty), + unit_of_measurement=record.uom, + status=record.status, + actual_cost=0, + purchase_order_amt=0, + purchase_order_amt_in_company_currency=0 ) procurement_record_against_mr.append(procurement_record_details) return mr_records, procurement_record_against_mr @@ -259,7 +274,7 @@ def get_po_entries(conditions): child.warehouse, child.material_request, child.material_request_item, - child.description, + child.item_code, child.stock_uom, child.qty, child.amount, diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index bebf0ccec5..c7204a1f34 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase): def test_result_for_procurement_tracker(self): filters = { 'company': '_Test Procurement Company', - 'cost_center': '_Test Cost Center - _TC' + 'cost_center': 'Main - _TPC' } expected_data = self.generate_expected_data() report = execute(filters) @@ -33,24 +33,27 @@ class TestProcurementTracker(unittest.TestCase): country="Pakistan" )).insert() warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company") - mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) + mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC") po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" - po.get("items")[0].cost_center = "_Test Cost Center - _TC" + po.get("items")[0].cost_center = "Main - _TPC" po.submit() pr = make_purchase_receipt(po.name) + pr.get("items")[0].cost_center = "Main - _TPC" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) + po.load_from_db() + expected_data = { "material_request_date": date_obj, - "cost_center": "_Test Cost Center - _TC", + "cost_center": "Main - _TPC", "project": None, "requesting_site": "_Test Procurement Warehouse - _TPC", "requestor": "Administrator", "material_request_no": mr.name, - "description": '_Test Item 1', + "item_code": '_Test Item', "quantity": 10.0, "unit_of_measurement": "_Test UOM", "status": "To Bill", @@ -58,9 +61,9 @@ class TestProcurementTracker(unittest.TestCase): "purchase_order": po.name, "supplier": "_Test Supplier", "estimated_cost": 0.0, - "actual_cost": None, - "purchase_order_amt": 5000.0, - "purchase_order_amt_in_company_currency": 300000.0, + "actual_cost": 0.0, + "purchase_order_amt": po.net_total, + "purchase_order_amt_in_company_currency": po.base_net_total, "expected_delivery_date": date_obj, "actual_delivery_date": date_obj } diff --git a/erpnext/change_log/v13/v13_0_0-beta_3.md b/erpnext/change_log/v13/v13_0_0-beta_3.md new file mode 100644 index 0000000000..09ea90087d --- /dev/null +++ b/erpnext/change_log/v13/v13_0_0-beta_3.md @@ -0,0 +1,66 @@ +### Version 13.0.0 Beta 3 Release Notes + +#### Features and Enhancements +- Dedicated Payroll module with Onboarding and Dashboard ([#21990](https://github.com/frappe/erpnext/pull/21990)) +- New Payroll Reports + - Income Tax Deductions + - Professional Tax Deductions + - Provident Fund Deductions + - Total Salary Payments Based on Payment Mode + - Salary Payments via ECS +- Distributed Cost Center ([#21531](https://github.com/frappe/erpnext/pull/21531)) +- More controlled deferred revenue booking ([#21671](https://github.com/frappe/erpnext/pull/21671)) + - Book deferred accounting via Journal Entry, provision to keep in draft + - Provision to book deferred revenue/expense on a monthly basis rather than by days +- Selling Desk, Dashboard and Onboarding ([#22055](https://github.com/frappe/erpnext/pull/22055)) +- Help Articles on support portal ([#22194](https://github.com/frappe/erpnext/pull/22194)) +- Issue Metrics and SLA Enhancements ([#21617](https://github.com/frappe/erpnext/pull/21617)) +- Multi UOM support in Request for Quotation ([#22249](https://github.com/frappe/erpnext/pull/22249)) +- The ability for a contract to be authorized internally using a signature field ([#22095](https://github.com/frappe/erpnext/pull/22095)) +- Gross Profit In Quotation ([#21795](https://github.com/frappe/erpnext/pull/21795)) +- Notify credit controller users for credit limit extension via Email ([#22213](https://github.com/frappe/erpnext/pull/22213)) +- Added In and Out time in attendance ([#21547](https://github.com/frappe/erpnext/pull/21547)) +- Renamed Loan Management to Loan on Desk Page ([#21877](https://github.com/frappe/erpnext/pull/21877)) +- Document tour to manufacturing settings ([#21962](https://github.com/frappe/erpnext/pull/21962)) +- Added Expense Approver field in Employee master ([#22244](https://github.com/frappe/erpnext/pull/22244)) + + +#### Fixes +- Bill all hours by default on Timesheet ([#22155](https://github.com/frappe/erpnext/pull/22155)) +- Unable to cancel employee advance ([#22374](https://github.com/frappe/erpnext/pull/22374)) +- Status error in purchase invoice ([#22351](https://github.com/frappe/erpnext/pull/22351)) +- Item-wise sales and purchase register export ([#22184](https://github.com/frappe/erpnext/pull/22184)) +- Billing address in for Purchase documents ([#22233](https://github.com/frappe/erpnext/pull/22233)) +- Handle canceled entries in financial statements ([#22231](https://github.com/frappe/erpnext/pull/22231)) +- Default period start date and period end date for financial statements ([#22011](https://github.com/frappe/erpnext/pull/22011)) +- Update Packed Items via Update Items in Sales Order ([#22392](https://github.com/frappe/erpnext/pull/22392)) +- Hide delete company transactions button if not system manager ([#21839](https://github.com/frappe/erpnext/pull/21839)) +- Skipping total row for tree-view reports ([#22350](https://github.com/frappe/erpnext/pull/22350)) +- Cancelled entries in tds payable monthly report ([#22131](https://github.com/frappe/erpnext/pull/22131)) +- Inter-company Invoice currency for multicurrency transactions ([#21984](https://github.com/frappe/erpnext/pull/21984)) +- Filter batches based on item and warehouse in Pick List (develop) ([#21780](https://github.com/frappe/erpnext/pull/21780)) +- Set cost center in Expense Claim child based on parent (if missing) ([#22175](https://github.com/frappe/erpnext/pull/22175)) +- Item wise backdated stock entry posting for immutable ledger ([#22366](https://github.com/frappe/erpnext/pull/22366)) +- Shopping cart UI fixes ([#22137](https://github.com/frappe/erpnext/pull/22137)) +- Filter Leave Type based on allocation for a particular employee ([#22050](https://github.com/frappe/erpnext/pull/22050)) +- Party validation for inter-warehouse transaction ([#22186](https://github.com/frappe/erpnext/pull/22186)) +- Manufacturing dashboard and work order summary chart ([#21946](https://github.com/frappe/erpnext/pull/21946)) +- IP Admission and Discharge, Minor fixes ([#21817](https://github.com/frappe/erpnext/pull/21817)) +- Validation of Purchase Order against Material Request missing ([#22192](https://github.com/frappe/erpnext/pull/22192)) +- Staffing Plan validation ([#22379](https://github.com/frappe/erpnext/pull/22379)) +- Do not allow backdated stock transactions in previous fiscal year ([#21967](https://github.com/frappe/erpnext/pull/21967)) +- Employee Advance Return not working ([#21812](https://github.com/frappe/erpnext/pull/21812)) +- Added card for reports on education desk ([#21853](https://github.com/frappe/erpnext/pull/21853)) +- Refactored project summary report ([#21943](https://github.com/frappe/erpnext/pull/21943)) +- Revenue and Customer Count only in date range in Customer Acquitition Report ([#22210](https://github.com/frappe/erpnext/pull/22210)) +- Alternative item not working for subcontract ([#22386](https://github.com/frappe/erpnext/pull/22386)) +- Unable to create batched Item ([#22393](https://github.com/frappe/erpnext/pull/22393)) +- Filters for the manufacturing reports ([#21960](https://github.com/frappe/erpnext/pull/21960)) +- Raw material warehouse in Production Planning Report ([#21982](https://github.com/frappe/erpnext/pull/21982)) +- Allowed LWP leave types to select in Leave Application even if there is no allocation against them ([#22197](https://github.com/frappe/erpnext/pull/22197)) +- Report not working on parameter Grade ([#21951](https://github.com/frappe/erpnext/pull/21951)) +- Allow to enter Relieving date if employee status is Left ([#22242](https://github.com/frappe/erpnext/pull/22242)) +- Resetting lost reason in opportunity and quotation ([#22378](https://github.com/frappe/erpnext/pull/22378)) +- Filtering issues in opening invoice creation tool ([#21969](https://github.com/frappe/erpnext/pull/21969)) +- Set default reference Id for "On Previous Row Amount" and "On Previous Row Total" ([#22346](https://github.com/frappe/erpnext/pull/22346)) +- UX date range field separated in from and to date fields. ([#21765](https://github.com/frappe/erpnext/pull/21765)) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 16b49a1e57..b06bb76ca8 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -1,8 +1,9 @@ from __future__ import unicode_literals +import frappe from frappe import _ def get_data(): - return [ + config = [ { "label": _("Purchasing"), "icon": "fa fa-star", @@ -243,3 +244,21 @@ def get_data(): }, ] + + regional = { + "label": _("Regional"), + "items": [ + { + "type": "doctype", + "name": "Import Supplier Invoice", + "description": _("Import Italian Supplier Invoice."), + "onboard": 1, + } + ] + } + + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + config.append(regional) + return config \ No newline at end of file diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index eecb143d55..ead503ed09 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -20,6 +20,7 @@ from erpnext.exceptions import InvalidCurrency from six import text_type from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.stock.get_item_details import get_item_warehouse +from erpnext.stock.doctype.packed_item.packed_item import make_packing_list force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") @@ -1137,8 +1138,8 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") @@ -1157,8 +1158,8 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item @@ -1190,6 +1191,26 @@ def check_and_delete_children(parent, data): @frappe.whitelist() def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): + def check_permissions(doc, perm_type='create'): + try: + doc.check_permission(perm_type) + except: + action = "add" if perm_type == 'create' else "update" + frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions")) + + def get_new_child_item(item_row): + if parent_doctype == "Sales Order": + return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) + if parent_doctype == "Purchase Order": + return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) + + def validate_quantity(child_item, d): + if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): + frappe.throw(_("Cannot set quantity less than delivered quantity")) + + if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty): + frappe.throw(_("Cannot set quantity less than received quantity")) + data = json.loads(trans_items) sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation'] @@ -1201,20 +1222,29 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil new_child_flag = False if not d.get("docname"): new_child_flag = True - if parent_doctype == "Sales Order": - child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) - if parent_doctype == "Purchase Order": - child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + check_permissions(parent, 'create') + child_item = get_new_child_item(d) else: + check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) - if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): + + prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate")) + prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty")) + prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor")) + + if parent_doctype == 'Sales Order': + prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") + elif parent_doctype == 'Purchase Order': + prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date") + + rate_unchanged = prev_rate == new_rate + qty_unchanged = prev_qty == new_qty + conversion_factor_unchanged = prev_con_fac == new_con_fac + date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc + if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged: continue - if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): - frappe.throw(_("Cannot set quantity less than delivered quantity")) - - if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty): - frappe.throw(_("Cannot set quantity less than received quantity")) + validate_quantity(child_item, d) child_item.qty = flt(d.get("qty")) precision = child_item.precision("rate") or 2 @@ -1225,6 +1255,18 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil else: child_item.rate = flt(d.get("rate")) + if d.get("conversion_factor"): + if child_item.stock_uom == child_item.uom: + child_item.conversion_factor = 1 + else: + child_item.conversion_factor = flt(d.get('conversion_factor')) + + if d.get("delivery_date") and parent_doctype == 'Sales Order': + child_item.delivery_date = d.get('delivery_date') + + if d.get("schedule_date") and parent_doctype == 'Purchase Order': + child_item.schedule_date = d.get('schedule_date') + if flt(child_item.price_list_rate): if flt(child_item.rate) > flt(child_item.price_list_rate): # if rate is greater than price_list_rate, set margin @@ -1260,6 +1302,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.set_qty_as_per_stock_uom() parent.calculate_taxes_and_totals() if parent_doctype == "Sales Order": + make_packing_list(parent) parent.set_gross_profit() frappe.get_doc('Authorization Control').validate_approving_authority(parent.doctype, parent.company, parent.base_grand_total) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 608e537e1b..89b48f07ee 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -349,7 +349,7 @@ class BuyingController(StockController): }) if not rm.rate: - rm.rate = get_valuation_rate(raw_material_data.item_code, self.supplier_warehouse, + rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency, company=self.company) rm.amount = qty * flt(rm.rate) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 29f8dd5702..1f95e00424 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -70,7 +70,7 @@ def validate_item_variant_attributes(item, args=None): else: attributes_list = attribute_values.get(attribute.lower(), []) - validate_item_attribute_value(attributes_list, attribute, value, item.name) + validate_item_attribute_value(attributes_list, attribute, value, item.name, from_variant=True) def validate_is_incremental(numeric_attribute, attribute, value, item): from_range = numeric_attribute.from_range @@ -93,13 +93,20 @@ def validate_is_incremental(numeric_attribute, attribute, value, item): .format(attribute, from_range, to_range, increment, item), InvalidItemAttributeValueError, title=_('Invalid Attribute')) -def validate_item_attribute_value(attributes_list, attribute, attribute_value, item): +def validate_item_attribute_value(attributes_list, attribute, attribute_value, item, from_variant=True): allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value') if allow_rename_attribute_value: pass elif attribute_value not in attributes_list: - frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format( - attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed')) + if from_variant: + frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format( + frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value")) + else: + msg = _("The value {0} is already assigned to an existing Item {1}.").format( + frappe.bold(attribute_value), frappe.bold(item)) + msg += "
" + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value")) + + frappe.throw(msg, InvalidItemAttributeValueError, title=_('Edit Not Allowed')) def get_attribute_values(item): if not frappe.flags.attribute_values: diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 1e0a48c134..b696ac39f6 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -361,7 +361,7 @@ class SellingController(StockController): self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no]))) def set_gross_profit(self): - if self.doctype == "Sales Order": + if self.doctype in ["Sales Order", "Quotation"]: for item in self.items: item.gross_profit = flt(((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item)) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 90d293088b..6af6feebe5 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,8 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - self.validate_inspection() + if not self.get('is_return'): + self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() @@ -226,8 +227,8 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) - + frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense Account in the Items table") + .format(item.idx, frappe.bold(item.item_code)), title=_("Expense Account Missing")) else: is_expense_account = frappe.db.get_value("Account", item.get("expense_account"), "report_type")=="Profit and Loss" diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py index ed379389d7..ecf041efd1 100644 --- a/erpnext/controllers/website_list_for_contact.py +++ b/erpnext/controllers/website_list_for_contact.py @@ -155,7 +155,7 @@ def has_website_permission(doc, ptype, user, verbose=False): return frappe.db.exists(doctype, get_customer_filter(doc, customers)) elif suppliers: fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier' - return frappe.db.exists(doctype, filters={ + return frappe.db.exists(doctype, { 'name': doc.name, fieldname: ["in", suppliers] }) diff --git a/erpnext/crm/doctype/contract/contract.json b/erpnext/crm/doctype/contract/contract.json index e04ad30ea7..0026e4a02e 100755 --- a/erpnext/crm/doctype/contract/contract.json +++ b/erpnext/crm/doctype/contract/contract.json @@ -29,6 +29,9 @@ "requires_fulfilment", "fulfilment_deadline", "fulfilment_terms", + "authorised_by_section", + "signee_company", + "signed_by_company", "sb_references", "document_type", "cb_links", @@ -223,10 +226,28 @@ "options": "Contract", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "signee_company", + "fieldtype": "Signature", + "label": "Signee (Company)" + }, + { + "fieldname": "signed_by_company", + "fieldtype": "Link", + "label": "Signed By (Company)", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "authorised_by_section", + "fieldtype": "Section Break", + "label": "Authorised By" } ], "is_submittable": 1, - "modified": "2019-09-30 00:56:41.559681", + "links": [], + "modified": "2020-03-30 06:56:07.257932", "modified_by": "Administrator", "module": "CRM", "name": "Contract", diff --git a/erpnext/crm/doctype/contract/contract.py b/erpnext/crm/doctype/contract/contract.py index 18444cc7e6..c39397bf4b 100644 --- a/erpnext/crm/doctype/contract/contract.py +++ b/erpnext/crm/doctype/contract/contract.py @@ -29,6 +29,9 @@ class Contract(Document): self.update_contract_status() self.update_fulfilment_status() + def before_submit(self): + self.signed_by_company = frappe.session.user + def before_update_after_submit(self): self.update_contract_status() self.update_fulfilment_status() diff --git a/erpnext/crm/doctype/contract_template/contract_template.json b/erpnext/crm/doctype/contract_template/contract_template.json index b883ce20c9..ef9974f863 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.json +++ b/erpnext/crm/doctype/contract_template/contract_template.json @@ -1,306 +1,106 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:title", - "beta": 0, - "creation": "2018-04-16 06:44:48.791312", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:title", + "creation": "2018-04-16 06:44:48.791312", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "contract_terms", + "sb_fulfilment", + "requires_fulfilment", + "fulfilment_terms" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_terms", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "contract_terms", + "fieldtype": "Text Editor", + "label": "Contract Terms and Conditions", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contract_terms", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contract Terms and Conditions", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "sb_fulfilment", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_fulfilment", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "requires_fulfilment", + "fieldtype": "Check", + "label": "Requires Fulfilment" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "requires_fulfilment", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Requires Fulfilment", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.requires_fulfilment==1", - "fieldname": "fulfilment_terms", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fulfilment Terms and Conditions", - "length": 0, - "no_copy": 0, - "options": "Contract Template Fulfilment Terms", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "depends_on": "eval:doc.requires_fulfilment==1", + "fieldname": "fulfilment_terms", + "fieldtype": "Table", + "label": "Fulfilment Terms and Conditions", + "options": "Contract Template Fulfilment Terms" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-04-17 07:36:05.217599", - "modified_by": "Administrator", - "module": "CRM", - "name": "Contract Template", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-06-03 00:24:58.179816", + "modified_by": "Administrator", + "module": "CRM", + "name": "Contract Template", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 6fef0c4643..f5f8b4efb3 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -6,6 +6,7 @@ "creation": "2013-04-10 11:45:37", "doctype": "DocType", "document_type": "Document", + "email_append_to": 1, "engine": "InnoDB", "field_order": [ "organization_lead", @@ -448,7 +449,7 @@ "idx": 5, "image_field": "image", "links": [], - "modified": "2020-05-11 20:27:45.868960", + "modified": "2020-06-18 14:39:41.835416", "modified_by": "Administrator", "module": "CRM", "name": "Lead", @@ -508,8 +509,10 @@ } ], "search_fields": "lead_name,lead_owner,status", + "sender_field": "email_id", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", + "subject_field": "title", "title_field": "title" } \ No newline at end of file diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 0c9ba495c7..f1b8171349 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -96,6 +96,7 @@ frappe.ui.form.on("Opportunity", { }); } else { frm.add_custom_button(__("Reopen"), function() { + frm.set_value("lost_reasons",[]) frm.set_value("status", "Open"); frm.save(); }); diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py index 9ed616afd2..68f0104fd6 100644 --- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py +++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py @@ -3,11 +3,7 @@ from frappe import _ def get_data(): return { - 'fieldname': 'prevdoc_docname', - 'non_standard_fieldnames': { - 'Supplier Quotation': 'opportunity', - 'Quotation': 'opportunity' - }, + 'fieldname': 'opportunity', 'transactions': [ { 'items': ['Quotation', 'Supplier Quotation'] diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.json b/erpnext/crm/doctype/social_media_post/social_media_post.json index 2601c14b4d..0a00dca280 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post.json +++ b/erpnext/crm/doctype/social_media_post/social_media_post.json @@ -30,24 +30,32 @@ "fieldname": "text", "fieldtype": "Small Text", "label": "Tweet", - "mandatory_depends_on": "eval:doc.twitter ==1" + "mandatory_depends_on": "eval:doc.twitter ==1", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "image", "fieldtype": "Attach Image", - "label": "Image" + "label": "Image", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "twitter", "fieldtype": "Check", - "label": "Twitter" + "label": "Twitter", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "linkedin", "fieldtype": "Check", - "label": "LinkedIn" + "label": "LinkedIn", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -56,13 +64,17 @@ "no_copy": 1, "options": "Social Media Post", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.twitter ==1", "fieldname": "content", "fieldtype": "Section Break", - "label": "Twitter" + "label": "Twitter", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -70,7 +82,9 @@ "fieldtype": "Select", "label": "Post Status", "options": "\nScheduled\nPosted\nError", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -78,7 +92,9 @@ "fieldtype": "Data", "hidden": 1, "label": "Twitter Post Id", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -86,68 +102,89 @@ "fieldtype": "Data", "hidden": 1, "label": "LinkedIn Post Id", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "campaign_name", "fieldtype": "Link", "in_list_view": 1, "label": "Campaign", - "options": "Campaign" + "options": "Campaign", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_6", "fieldtype": "Column Break", - "label": "Share On" + "label": "Share On", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_14", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tweet_preview", - "fieldtype": "HTML" + "fieldtype": "HTML", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "depends_on": "eval:doc.linkedin==1", "fieldname": "linkedin_section", "fieldtype": "Section Break", - "label": "LinkedIn" + "label": "LinkedIn", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "attachments_section", "fieldtype": "Section Break", - "label": "Attachments" + "label": "Attachments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "linkedin_post", "fieldtype": "Text", "label": "Post", - "mandatory_depends_on": "eval:doc.linkedin ==1" + "mandatory_depends_on": "eval:doc.linkedin ==1", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "scheduled_time", "fieldtype": "Datetime", "label": "Scheduled Time", - "read_only_depends_on": "eval:doc.post_status == \"Posted\"" + "read_only_depends_on": "eval:doc.post_status == \"Posted\"", + "show_days": 1, + "show_seconds": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-21 15:10:04.953713", + "modified": "2020-06-14 10:31:33.961381", "modified_by": "Administrator", "module": "CRM", "name": "Social Media Post", "owner": "Administrator", "permissions": [ { + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -157,6 +194,35 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "submit": 1, "write": 1 } ], diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index c43fcaef8c..44d672a7b5 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-28 13:59:33.693420", + "modified": "2020-05-28 21:07:41.278784", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -35,8 +35,8 @@ "step": "Create and Send Quotation" } ], - "subtitle": "Lead, Opportunity, Customer and more", - "success_message": "CRM Module is all setup!", - "title": "Let's Set Up Your CRM", + "subtitle": "Lead, Opportunity, Customer and more.", + "success_message": "CRM Module is all Set Up!", + "title": "Let's Set Up Your CRM.", "user_can_dismiss": 1 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json index 9201d77cf8..78f7e4de9c 100644 --- a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-27 11:30:28.237263", + "modified": "2020-05-28 21:07:11.461172", "modified_by": "Administrator", "name": "Create and Send Quotation", "owner": "Administrator", diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json index 6ff0bd61f1..c45e8b036c 100644 --- a/erpnext/crm/onboarding_step/create_lead/create_lead.json +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-27 11:30:59.493720", + "modified": "2020-05-28 21:07:01.373403", "modified_by": "Administrator", "name": "Create Lead", "owner": "Administrator", diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json index 545a756a59..fa26921ae2 100644 --- a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-27 11:28:07.452857", + "modified": "2020-05-14 17:28:16.448676", "modified_by": "Administrator", "name": "Introduction to CRM", "owner": "Administrator", diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py new file mode 100644 index 0000000000..c36dfb11b5 --- /dev/null +++ b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py @@ -0,0 +1,17 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'assessment_plan', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Assessment'), + 'items': ['Assessment Result'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py new file mode 100644 index 0000000000..752af29a9d --- /dev/null +++ b/erpnext/education/doctype/course/course_dashboard.py @@ -0,0 +1,25 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'course', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Course'), + 'items': ['Course Enrollment', 'Course Schedule'] + }, + { + 'label': _('Student'), + 'items': ['Student Group'] + }, + { + 'label': _('Assessment'), + 'items': ['Assessment Plan'] + }, + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.json b/erpnext/education/doctype/fee_schedule/fee_schedule.json index 1e98709b2b..791831810a 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.json +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2017-07-18 15:21:21.527136", @@ -7,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "fee_structure", + "posting_date", "due_date", "naming_series", "fee_creation_status", @@ -259,10 +261,18 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "reqd": 1 } ], "is_submittable": 1, - "modified": "2019-05-26 09:10:34.522409", + "links": [], + "modified": "2020-05-15 08:39:20.682837", "modified_by": "Administrator", "module": "Education", "name": "Fee Schedule", diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.py b/erpnext/education/doctype/fee_schedule/fee_schedule.py index a42800a80d..1543acdca9 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.py +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.py @@ -87,6 +87,7 @@ def generate_fee(fee_schedule): } } }) + fees_doc.posting_date = doc.posting_date fees_doc.student = student.student fees_doc.student_name = student.student_name fees_doc.program = student.program diff --git a/erpnext/education/doctype/fee_structure/fee_structure.js b/erpnext/education/doctype/fee_structure/fee_structure.js index 7606565fad..f09d2efcb9 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure.js +++ b/erpnext/education/doctype/fee_structure/fee_structure.js @@ -9,6 +9,14 @@ frappe.ui.form.on('Fee Structure', { }, onload: function(frm) { + frm.set_query("academic_term", function() { + return { + "filters": { + "academic_year": frm.doc.academic_year + } + }; + }); + frm.set_query("receivable_account", function(doc) { return { filters: { diff --git a/erpnext/education/doctype/fee_structure/fee_structure.json b/erpnext/education/doctype/fee_structure/fee_structure.json index 8ff6851d90..67e46372f8 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure.json +++ b/erpnext/education/doctype/fee_structure/fee_structure.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -11,8 +12,8 @@ "program", "student_category", "column_break_2", - "academic_term", "academic_year", + "academic_term", "section_break_4", "components", "section_break_6", @@ -157,7 +158,8 @@ ], "icon": "fa fa-flag", "is_submittable": 1, - "modified": "2019-05-26 09:04:17.765758", + "links": [], + "modified": "2020-06-16 15:34:57.295010", "modified_by": "Administrator", "module": "Education", "name": "Fee Structure", diff --git a/erpnext/education/doctype/fees/fees.js b/erpnext/education/doctype/fees/fees.js index 17ef44954b..867866fbf1 100644 --- a/erpnext/education/doctype/fees/fees.js +++ b/erpnext/education/doctype/fees/fees.js @@ -55,14 +55,15 @@ frappe.ui.form.on("Fees", { frm.set_df_property('posting_date', 'read_only', 1); frm.set_df_property('posting_time', 'read_only', 1); } - if(frm.doc.docstatus===1) { + if(frm.doc.docstatus > 0) { frm.add_custom_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: frm.doc.name, from_date: frm.doc.posting_date, - to_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), company: frm.doc.company, - group_by_voucher: false + group_by: '', + show_cancelled_entries: frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __("View")); diff --git a/erpnext/education/doctype/program_course/program_course.json b/erpnext/education/doctype/program_course/program_course.json index a24e88a861..940358e4e9 100644 --- a/erpnext/education/doctype/program_course/program_course.json +++ b/erpnext/education/doctype/program_course/program_course.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2015-09-07 14:37:01.886859", "doctype": "DocType", "editable_grid": 1, @@ -16,26 +17,33 @@ "in_list_view": 1, "label": "Course", "options": "Course", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, - { + { + "fetch_from": "course.course_name", "fieldname": "course_name", "fieldtype": "Data", "in_list_view": 1, "label": "Course Name", - "fetch_from": "course.course_name", - "read_only":1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "required", "fieldtype": "Check", "in_list_view": 1, - "label": "Mandatory" + "label": "Mandatory", + "show_days": 1, + "show_seconds": 1 } ], "istable": 1, - "modified": "2019-06-12 12:42:12.845972", + "links": [], + "modified": "2020-06-09 18:56:10.213241", "modified_by": "Administrator", "module": "Education", "name": "Program Course", @@ -45,4 +53,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/education/doctype/student_admission/student_admission.json b/erpnext/education/doctype/student_admission/student_admission.json index b3c10d4331..1096888d4d 100644 --- a/erpnext/education/doctype/student_admission/student_admission.json +++ b/erpnext/education/doctype/student_admission/student_admission.json @@ -1,398 +1,119 @@ { - "allow_copy": 0, - "allow_guest_to_view": 1, - "allow_import": 0, - "allow_rename": 1, - "autoname": "", - "beta": 0, - "creation": "2016-09-13 03:05:27.154713", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "allow_guest_to_view": 1, + "allow_rename": 1, + "creation": "2016-09-13 03:05:27.154713", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "route", + "column_break_3", + "academic_year", + "admission_start_date", + "admission_end_date", + "published", + "enable_admission_application", + "section_break_5", + "program_details", + "introduction" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "route", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Route", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "fieldname": "route", + "fieldtype": "Data", + "label": "Route", + "no_copy": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "application_form_route", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Application Form Route", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "academic_year", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Academic Year", + "no_copy": 1, + "options": "Academic Year", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "academic_year", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Academic Year", - "length": 0, - "no_copy": 1, - "options": "Academic Year", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "admission_start_date", + "fieldtype": "Date", + "label": "Admission Start Date", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Admission Start Date", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "admission_end_date", + "fieldtype": "Date", + "label": "Admission End Date", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_end_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Admission End Date", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "published", + "fieldtype": "Check", + "label": "Publish on website" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "published", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Publish on website", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Eligibility and Details" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Eligibility and Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "program_details", + "fieldtype": "Table", + "label": "Eligibility and Details", + "options": "Student Admission Program" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_details", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Eligibility and Details", - "length": 0, - "no_copy": 0, - "options": "Student Admission Program", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "introduction", + "fieldtype": "Text Editor", + "label": "Introduction" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "introduction", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Introduction", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "default": "0", + "fieldname": "enable_admission_application", + "fieldtype": "Check", + "label": "Enable Admission Application" } - ], - "has_web_view": 1, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_published_field": "published", - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-11-10 18:57:34.570376", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Admission", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 1, + "is_published_field": "published", + "links": [], + "modified": "2020-06-15 20:18:38.591626", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Admission", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "route": "admissions", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 0, - "track_seen": 0 + ], + "restrict_to_domain": "Education", + "route": "admissions", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title" } \ No newline at end of file diff --git a/erpnext/education/doctype/student_admission/templates/student_admission.html b/erpnext/education/doctype/student_admission/templates/student_admission.html index 25afaca84d..e5a9ead31e 100644 --- a/erpnext/education/doctype/student_admission/templates/student_admission.html +++ b/erpnext/education/doctype/student_admission/templates/student_admission.html @@ -43,8 +43,8 @@ Program/Std. - Minumum Age(DOB) - Maximum Age(DOB) + Minumum Age + Maximum Age Application Fee @@ -52,8 +52,8 @@ {% for row in program_details %} {{ row.program }} - {{ row.minimum_age }} - {{ row.maximum_age }} + {{ row.min_age }} + {{ row.max_age }} {{ row.application_fee }} {% endfor %} @@ -61,12 +61,11 @@ {% endif %} - - {%- if application_form_route -%} + {%- if doc.enable_admission_application -%}

+ href='/student-applicant?new=1&student_admission={{doc.name}}'> {{ _("Apply Now") }}

{% endif %} diff --git a/erpnext/education/doctype/student_admission/test_student_admission.js b/erpnext/education/doctype/student_admission/test_student_admission.js index ed794b2482..3a0bb0b2f2 100644 --- a/erpnext/education/doctype/student_admission/test_student_admission.js +++ b/erpnext/education/doctype/student_admission/test_student_admission.js @@ -11,7 +11,7 @@ QUnit.test('Test: Student Admission', function(assert) { {admission_start_date: '2016-04-20'}, {admission_end_date: '2016-05-31'}, {title: '2016-17 Admissions'}, - {application_form_route: 'student-applicant'}, + {enable_admission_application: 1}, {introduction: 'Test intro'}, {program_details: [ [ @@ -28,7 +28,7 @@ QUnit.test('Test: Student Admission', function(assert) { assert.ok(cur_frm.doc.admission_start_date == '2016-04-20'); assert.ok(cur_frm.doc.admission_end_date == '2016-05-31'); assert.ok(cur_frm.doc.title == '2016-17 Admissions'); - assert.ok(cur_frm.doc.application_form_route == 'student-applicant'); + assert.ok(cur_frm.doc.enable_admission_application == 1); assert.ok(cur_frm.doc.introduction == 'Test intro'); assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected'); assert.ok(cur_frm.doc.program_details[0].application_fee == 1000); diff --git a/erpnext/education/doctype/student_admission_program/student_admission_program.json b/erpnext/education/doctype/student_admission_program/student_admission_program.json index 97b1bba421..e9f041e101 100644 --- a/erpnext/education/doctype/student_admission_program/student_admission_program.json +++ b/erpnext/education/doctype/student_admission_program/student_admission_program.json @@ -1,237 +1,77 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2017-09-15 12:59:43.207923", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-09-15 12:59:43.207923", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "program", + "min_age", + "max_age", + "column_break_4", + "application_fee", + "applicant_naming_series" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Program", - "length": 0, - "no_copy": 0, - "options": "Program", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "program", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Program", + "options": "Program", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "minimum_age", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Minimum Age", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maximum_age", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maximum Age", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "application_fee", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Application Fee", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "applicant_naming_series", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Naming Series (for Student Applicant)", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "application_fee", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Application Fee", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "min_age", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Minimum Age", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "applicant_naming_series", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Naming Series (for Student Applicant)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "max_age", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Maximum Age", + "show_days": 1, + "show_seconds": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:37:17.408427", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Admission Program", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-06-10 23:06:30.037404", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Admission Program", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Education", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/education/doctype/student_applicant/student_applicant.py b/erpnext/education/doctype/student_applicant/student_applicant.py index ab947807dd..211348201e 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.py +++ b/erpnext/education/doctype/student_applicant/student_applicant.py @@ -6,7 +6,7 @@ from __future__ import print_function, unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import getdate +from frappe.utils import getdate, add_years, nowdate, date_diff class StudentApplicant(Document): def autoname(self): @@ -31,6 +31,7 @@ class StudentApplicant(Document): def validate(self): self.validate_dates() self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) + if self.student_admission and self.program and self.date_of_birth: self.validation_from_student_admission() @@ -48,16 +49,16 @@ class StudentApplicant(Document): frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant")) def validation_from_student_admission(self): + student_admission = get_student_admission_data(self.student_admission, self.program) - # different validation for minimum and maximum age so that either min/max can also work independently. - if student_admission and student_admission.minimum_age and \ - getdate(student_admission.minimum_age) < getdate(self.date_of_birth): - frappe.throw(_("Not eligible for the admission in this program as per DOB")) + if student_admission and student_admission.min_age and \ + date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.min_age)) < 0: + frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth")) - if student_admission and student_admission.maximum_age and \ - getdate(student_admission.maximum_age) > getdate(self.date_of_birth): - frappe.throw(_("Not eligible for the admission in this program as per DOB")) + if student_admission and student_admission.max_age and \ + date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.max_age)) > 0: + frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth")) def on_payment_authorized(self, *args, **kwargs): @@ -65,10 +66,12 @@ class StudentApplicant(Document): def get_student_admission_data(student_admission, program): + student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date, - sap.program, sap.minimum_age, sap.maximum_age, sap.applicant_naming_series + sap.program, sap.min_age, sap.max_age, sap.applicant_naming_series from `tabStudent Admission` sa, `tabStudent Admission Program` sap where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1) + if student_admission: return student_admission[0] else: diff --git a/erpnext/education/web_form/student_applicant/student_applicant.json b/erpnext/education/web_form/student_applicant/student_applicant.json index b1ad754c32..1810f07a05 100644 --- a/erpnext/education/web_form/student_applicant/student_applicant.json +++ b/erpnext/education/web_form/student_applicant/student_applicant.json @@ -1,200 +1,248 @@ { - "accept_payment": 0, - "allow_comments": 0, - "allow_delete": 0, - "allow_edit": 1, - "allow_incomplete": 0, - "allow_multiple": 1, - "allow_print": 0, - "amount": 0.0, - "amount_based_on_field": 0, - "creation": "2016-09-22 13:10:10.792735", - "doc_type": "Student Applicant", - "docstatus": 0, - "doctype": "Web Form", - "idx": 0, - "is_standard": 1, - "login_required": 1, - "max_attachment_size": 0, - "modified": "2017-02-21 05:44:46.022738", - "modified_by": "Administrator", - "module": "Education", - "name": "student-applicant", - "owner": "Administrator", - "payment_button_label": "Buy Now", - "published": 1, - "route": "student-applicant", - "show_sidebar": 1, - "sidebar_items": [], - "success_url": "/student-applicant", - "title": "Student Applicant", + "accept_payment": 0, + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 1, + "allow_print": 0, + "amount": 0.0, + "amount_based_on_field": 0, + "creation": "2016-09-22 13:10:10.792735", + "doc_type": "Student Applicant", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "is_standard": 1, + "login_required": 1, + "max_attachment_size": 0, + "modified": "2020-06-11 22:53:45.875310", + "modified_by": "Administrator", + "module": "Education", + "name": "student-applicant", + "owner": "Administrator", + "payment_button_label": "Buy Now", + "published": 1, + "route": "student-applicant", + "route_to_success_link": 0, + "show_attachments": 0, + "show_in_grid": 0, + "show_sidebar": 1, + "sidebar_items": [], + "success_url": "/student-applicant", + "title": "Student Applicant", "web_form_fields": [ { - "fieldname": "first_name", - "fieldtype": "Data", - "hidden": 0, - "label": "First Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 1 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "first_name", + "fieldtype": "Data", + "hidden": 0, + "label": "First Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, { - "fieldname": "middle_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Middle Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "middle_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Middle Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "last_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Last Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "last_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Last Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "image", - "fieldtype": "Data", - "hidden": 0, - "label": "Image", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "image", + "fieldtype": "Data", + "hidden": 0, + "label": "Image", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "program", - "fieldtype": "Link", - "hidden": 0, - "label": "Program", - "max_length": 0, - "max_value": 0, - "options": "Program", - "read_only": 0, - "reqd": 1 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "program", + "fieldtype": "Link", + "hidden": 0, + "label": "Program", + "max_length": 0, + "max_value": 0, + "options": "Program", + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, { - "fieldname": "academic_year", - "fieldtype": "Link", - "hidden": 0, - "label": "Academic Year", - "max_length": 0, - "max_value": 0, - "options": "Academic Year", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "academic_year", + "fieldtype": "Link", + "hidden": 0, + "label": "Academic Year", + "max_length": 0, + "max_value": 0, + "options": "Academic Year", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "date_of_birth", - "fieldtype": "Date", - "hidden": 0, - "label": "Date of Birth", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "date_of_birth", + "fieldtype": "Date", + "hidden": 0, + "label": "Date of Birth", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "blood_group", - "fieldtype": "Select", - "hidden": 0, - "label": "Blood Group", - "max_length": 0, - "max_value": 0, - "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "blood_group", + "fieldtype": "Select", + "hidden": 0, + "label": "Blood Group", + "max_length": 0, + "max_value": 0, + "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "student_email_id", - "fieldtype": "Data", - "hidden": 0, - "label": "Student Email ID", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "student_email_id", + "fieldtype": "Data", + "hidden": 0, + "label": "Student Email ID", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "student_mobile_number", - "fieldtype": "Data", - "hidden": 0, - "label": "Student Mobile Number", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "student_mobile_number", + "fieldtype": "Data", + "hidden": 0, + "label": "Student Mobile Number", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "default": "INDIAN", - "fieldname": "nationality", - "fieldtype": "Data", - "hidden": 0, - "label": "Nationality", - "max_length": 0, - "max_value": 0, - "options": "", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "default": "INDIAN", + "fieldname": "nationality", + "fieldtype": "Data", + "hidden": 0, + "label": "Nationality", + "max_length": 0, + "max_value": 0, + "options": "", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "address_line_1", - "fieldtype": "Data", - "hidden": 0, - "label": "Address Line 1", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "address_line_1", + "fieldtype": "Data", + "hidden": 0, + "label": "Address Line 1", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "address_line_2", - "fieldtype": "Data", - "hidden": 0, - "label": "Address Line 2", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "address_line_2", + "fieldtype": "Data", + "hidden": 0, + "label": "Address Line 2", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "pincode", - "fieldtype": "Data", - "hidden": 0, - "label": "Pincode", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "pincode", + "fieldtype": "Data", + "hidden": 0, + "label": "Pincode", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "guardians", - "fieldtype": "Table", - "hidden": 0, - "label": "Guardians", - "max_length": 0, - "max_value": 0, - "options": "Student Guardian", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "guardians", + "fieldtype": "Table", + "hidden": 0, + "label": "Guardians", + "max_length": 0, + "max_value": 0, + "options": "Student Guardian", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "siblings", - "fieldtype": "Table", - "hidden": 0, - "label": "Siblings", - "max_length": 0, - "max_value": 0, - "options": "Student Sibling", - "read_only": 0, - "reqd": 0 + "allow_read_on_all_link_options": 0, + "fieldname": "siblings", + "fieldtype": "Table", + "hidden": 0, + "label": "Siblings", + "max_length": 0, + "max_value": 0, + "options": "Student Sibling", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "student_admission", + "fieldtype": "Link", + "hidden": 0, + "label": "Student Admission", + "max_length": 0, + "max_value": 0, + "options": "Student Admission", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 } ] } \ No newline at end of file diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index ca0e1609cb..d59f909298 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -95,10 +95,10 @@ def create_sales_order(shopify_order, shopify_settings, company=None): items = get_order_items(shopify_order.get("line_items"), shopify_settings) if not items: - message = 'Following items are exists in order but relevant record not found in Product master' + message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master' message += "\n" + ", ".join(product_not_exists) - make_shopify_log(status="Error", exception=e, rollback=True) + make_shopify_log(status="Error", exception=message, rollback=True) return '' @@ -241,14 +241,17 @@ def get_order_taxes(shopify_order, shopify_settings): return taxes def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings): + """Shipping lines represents the shipping details, + each such shipping detail consists of a list of tax_lines""" for shipping_charge in shipping_lines: - taxes.append({ - "charge_type": _("Actual"), - "account_head": get_tax_account_head(shipping_charge), - "description": shipping_charge["title"], - "tax_amount": shipping_charge["price"], - "cost_center": shopify_settings.cost_center - }) + for tax in shipping_charge.get("tax_lines"): + taxes.append({ + "charge_type": _("Actual"), + "account_head": get_tax_account_head(tax), + "description": tax["title"], + "tax_amount": tax["price"], + "cost_center": shopify_settings.cost_center + }) return taxes diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 44f87e0462..6dedaa8c53 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -73,10 +73,16 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name if customer_exists: frappe.rename_doc("Customer", old_name, customer_name) - billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) - shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) - rename_address(billing_address, customer) - rename_address(shipping_address, customer) + for address_type in ("Billing", "Shipping",): + try: + address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type}) + rename_address(address, customer) + except ( + frappe.DoesNotExistError, + frappe.DuplicateEntryError, + frappe.ValidationError, + ): + pass else: create_address(raw_billing_data, customer, "Billing") create_address(raw_shipping_data, customer, "Shipping") @@ -182,7 +188,8 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr') default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr) - if not frappe.db.exists("Warehouse", default_warehouse): + if not frappe.db.exists("Warehouse", default_warehouse) \ + and not woocommerce_settings.warehouse: frappe.throw(_("Please set Warehouse in Woocommerce Settings")) for item in order.get("line_items"): diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index a7062239c3..c3371ed5df 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -124,10 +124,11 @@ def add_account_subtype(account_subtype): @frappe.whitelist() def sync_transactions(bank, bank_account): - - last_sync_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") - if last_sync_date: - start_date = formatdate(last_sync_date, "YYYY-MM-dd") + '''Sync transactions based on the last integration date as the start date, after the sync is completed + add the transaction date of the oldest transaction as the last integration date''' + last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") + if last_transaction_date: + start_date = formatdate(last_transaction_date, "YYYY-MM-dd") else: start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd") end_date = formatdate(today(), "YYYY-MM-dd") @@ -139,12 +140,14 @@ def sync_transactions(bank, bank_account): for transaction in reversed(transactions): result += new_bank_transaction(transaction) - frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( - len(result), bank_account, start_date, end_date)) + if result: + last_transaction_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date') - frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date)) + frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( + len(result), bank_account, start_date, end_date)) + + frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date) - return result except Exception: frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index 8f1b746e5b..5339c99155 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -133,7 +133,7 @@ "label": "Customer Settings" }, { - "description": "If Shopify not contains a customer in Order, then while syncing Orders, the system will consider default customer for order", + "description": "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order", "fieldname": "default_customer", "fieldtype": "Link", "label": "Default Customer", @@ -258,7 +258,7 @@ } ], "issingle": 1, - "modified": "2019-09-13 12:32:11.384757", + "modified": "2020-05-28 12:32:11.384757", "modified_by": "umair@erpnext.com", "module": "ERPNext Integrations", "name": "Shopify Settings", @@ -277,4 +277,4 @@ ], "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index 64c3b2d273..25ffd28109 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -8,6 +8,7 @@ import json from frappe import _ from frappe.model.document import Document from frappe.utils import get_request_session +from requests.exceptions import HTTPError from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from erpnext.erpnext_integrations.utils import get_webhook_address from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log @@ -29,19 +30,24 @@ class ShopifySettings(Document): webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] # url = get_shopify_url('admin/webhooks.json', self) created_webhooks = [d.method for d in self.webhooks] - url = get_shopify_url('admin/api/2019-04/webhooks.json', self) + url = get_shopify_url('admin/api/2020-04/webhooks.json', self) for method in webhooks: session = get_request_session() try: - d = session.post(url, data=json.dumps({ + res = session.post(url, data=json.dumps({ "webhook": { "topic": method, "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'), "format": "json" } }), headers=get_header(self)) - d.raise_for_status() - self.update_webhook_table(method, d.json()) + res.raise_for_status() + self.update_webhook_table(method, res.json()) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: make_shopify_log(status="Warning", exception=e, rollback=True) @@ -50,13 +56,18 @@ class ShopifySettings(Document): deleted_webhooks = [] for d in self.webhooks: - url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self) + url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self) try: res = session.delete(url, headers=get_header(self)) res.raise_for_status() deleted_webhooks.append(d) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: - frappe.log_error(message=frappe.get_traceback(), title=e) + frappe.log_error(message=e, title='Shopify Webhooks Issue') for d in deleted_webhooks: self.remove(d) @@ -125,4 +136,3 @@ def setup_custom_fields(): } create_custom_fields(custom_fields) - diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py index bde101123d..f9f0bb3cec 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py @@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo shopify_variants_attr_list = ["option1", "option2", "option3"] def sync_item_from_shopify(shopify_settings, item): - url = get_shopify_url("admin/api/2019-04/products/{0}.json".format(item.get("product_id")), shopify_settings) + url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings) session = get_request_session() try: diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d84c8234ef..fd16d1e84a 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -1,7 +1,9 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Tally Migration', { +frappe.provide("erpnext.tally_migration"); + +frappe.ui.form.on("Tally Migration", { onload: function (frm) { let reload_status = true; frappe.realtime.on("tally_migration_progress_update", function (data) { @@ -35,7 +37,17 @@ frappe.ui.form.on('Tally Migration', { } }); }, + refresh: function (frm) { + frm.trigger("show_logs_preview"); + erpnext.tally_migration.failed_import_log = JSON.parse(frm.doc.failed_import_log); + erpnext.tally_migration.fixed_errors_log = JSON.parse(frm.doc.fixed_errors_log); + + ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { + frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) + frm.toggle_enable(account, frm.doc.is_day_book_data_processed != 1) + }) + if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -47,6 +59,7 @@ frappe.ui.form.on('Tally Migration', { } } } + if (frm.doc.day_book_data && !frm.doc.is_day_book_data_imported) { if (frm.doc.is_day_book_data_processed) { if (frm.doc.status != "Importing Day Book Data") { @@ -59,6 +72,17 @@ frappe.ui.form.on('Tally Migration', { } } }, + + erpnext_company: function (frm) { + frappe.db.exists("Company", frm.doc.erpnext_company).then(exists => { + if (exists) { + frappe.msgprint( + __("Company {0} already exists. Continuing will overwrite the Company and Chart of Accounts", [frm.doc.erpnext_company]), + ); + } + }); + }, + add_button: function (frm, label, method) { frm.add_custom_button( label, @@ -71,5 +95,255 @@ frappe.ui.form.on('Tally Migration', { frm.reload_doc(); } ); + }, + + render_html_table(frm, shown_logs, hidden_logs, field) { + if (shown_logs && shown_logs.length > 0) { + frm.toggle_display(field, true); + } else { + frm.toggle_display(field, false); + return + } + let rows = erpnext.tally_migration.get_html_rows(shown_logs, field); + let rows_head, table_caption; + + let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? ` + And ${hidden_logs.length} more others + `: ""; + + if (field === "fixed_error_log_preview") { + rows_head = `${__("Meta Data")} + ${__("Unresolve")}` + table_caption = "Resolved Issues" + } else { + rows_head = `${__("Error Message")} + ${__("Create")}` + table_caption = "Error Log" + } + + frm.get_field(field).$wrapper.html(` + + + + + + ${rows_head} + + ${rows} + ${table_footer} +
${table_caption}
${__("#")}${__("DocType")}
+ `); + }, + + show_error_summary(frm) { + let summary = erpnext.tally_migration.failed_import_log.reduce((summary, row) => { + if (row.doc) { + if (summary[row.doc.doctype]) { + summary[row.doc.doctype] += 1; + } else { + summary[row.doc.doctype] = 1; + } + } + return summary + }, {}); + console.table(summary); + }, + + show_logs_preview(frm) { + let empty = "[]"; + let import_log = frm.doc.failed_import_log || empty; + let completed_log = frm.doc.fixed_errors_log || empty; + let render_section = !(import_log === completed_log && import_log === empty); + + frm.toggle_display("import_log_section", render_section); + if (render_section) { + frm.trigger("show_error_summary"); + frm.trigger("show_errored_import_log"); + frm.trigger("show_fixed_errors_log"); + } + }, + + show_errored_import_log(frm) { + let import_log = erpnext.tally_migration.failed_import_log; + let logs = import_log.slice(0, 20); + let hidden_logs = import_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "failed_import_preview"); + }, + + show_fixed_errors_log(frm) { + let completed_log = erpnext.tally_migration.fixed_errors_log; + let logs = completed_log.slice(0, 20); + let hidden_logs = completed_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "fixed_error_log_preview"); } }); + +erpnext.tally_migration.getError = (traceback) => { + /* Extracts the Error Message from the Python Traceback or Solved error */ + let is_multiline = traceback.trim().indexOf("\n") != -1; + let message; + + if (is_multiline) { + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 + let error_line = traceback.substr(exc_error_idx) + let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + message = error_line.slice(split_str_idx).trim(); + } else { + message = traceback; + } + + return message +} + +erpnext.tally_migration.cleanDoc = (obj) => { + /* Strips all null and empty values of your JSON object */ + let temp = obj; + $.each(temp, function(key, value){ + if (value === "" || value === null){ + delete obj[key]; + } else if (Object.prototype.toString.call(value) === '[object Object]') { + erpnext.tally_migration.cleanDoc(value); + } else if ($.isArray(value)) { + $.each(value, function (k,v) { erpnext.tally_migration.cleanDoc(v); }); + } + }); + return temp; +} + +erpnext.tally_migration.unresolve = (document) => { + /* Mark document migration as unresolved ie. move to failed error log */ + let frm = cur_frm; + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; + + let modified_fixed_log = fixed_log.filter(row => { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { + return row + } + }); + + failed_log.push({ doc: document, exc: `Marked unresolved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(failed_log); + frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log); + + frm.dirty(); + frm.save(); +} + +erpnext.tally_migration.resolve = (document) => { + /* Mark document migration as resolved ie. move to fixed error log */ + let frm = cur_frm; + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; + + let modified_failed_log = failed_log.filter(row => { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { + return row + } + }); + fixed_log.push({ doc: document, exc: `Solved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(modified_failed_log); + frm.doc.fixed_errors_log = JSON.stringify(fixed_log); + + frm.dirty(); + frm.save(); +} + +erpnext.tally_migration.create_new_doc = (document) => { + /* Mark as resolved and create new document */ + erpnext.tally_migration.resolve(document); + return frappe.call({ + type: "POST", + method: 'erpnext.erpnext_integrations.doctype.tally_migration.tally_migration.new_doc', + args: { + document + }, + freeze: true, + callback: function(r) { + if(!r.exc) { + frappe.model.sync(r.message); + frappe.get_doc(r.message.doctype, r.message.name).__run_link_triggers = true; + frappe.set_route("Form", r.message.doctype, r.message.name); + } + } + }); +} + +erpnext.tally_migration.get_html_rows = (logs, field) => { + let index = 0; + let rows = logs + .map(({ doc, exc }) => { + let id = frappe.dom.get_unique_id(); + let traceback = exc; + + let error_message = erpnext.tally_migration.getError(traceback); + index++; + + let show_traceback = ` + +
+
+
${traceback}
+
+
`; + + let show_doc = ` + +
+
+
${JSON.stringify(erpnext.tally_migration.cleanDoc(doc), null, 1)}
+
+
`; + + let create_button = ` + ` + + let mark_as_unresolved = ` + ` + + if (field === "fixed_error_log_preview") { + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${show_doc}
+ + +
${mark_as_unresolved}
+ + `; + } else { + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${show_traceback}
+
${show_doc}
+ + +
${create_button}
+ + `; + } + }).join(""); + + return rows +} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index dc6f093ac9..417d943792 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -28,14 +28,19 @@ "vouchers", "accounts_section", "default_warehouse", - "round_off_account", + "default_round_off_account", "column_break_21", "default_cost_center", "day_book_section", "day_book_data", "column_break_27", "is_day_book_data_processed", - "is_day_book_data_imported" + "is_day_book_data_imported", + "import_log_section", + "failed_import_log", + "fixed_errors_log", + "failed_import_preview", + "fixed_error_log_preview" ], "fields": [ { @@ -57,6 +62,7 @@ "fieldname": "tally_creditors_account", "fieldtype": "Data", "label": "Tally Creditors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -69,6 +75,7 @@ "fieldname": "tally_debtors_account", "fieldtype": "Data", "label": "Tally Debtors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -92,7 +99,7 @@ "fieldname": "erpnext_company", "fieldtype": "Data", "label": "ERPNext Company", - "read_only_depends_on": "eval:doc.is_master_data_processed == 1" + "read_only_depends_on": "eval:doc.is_master_data_processed==1" }, { "fieldname": "processed_files_section", @@ -136,6 +143,7 @@ }, { "depends_on": "is_master_data_imported", + "description": "The accounts are set by the system automatically but do confirm these defaults", "fieldname": "accounts_section", "fieldtype": "Section Break", "label": "Accounts" @@ -146,12 +154,6 @@ "label": "Default Warehouse", "options": "Warehouse" }, - { - "fieldname": "round_off_account", - "fieldtype": "Link", - "label": "Round Off Account", - "options": "Account" - }, { "fieldname": "column_break_21", "fieldtype": "Column Break" @@ -212,11 +214,47 @@ "fieldname": "default_uom", "fieldtype": "Link", "label": "Default UOM", - "options": "UOM" + "options": "UOM", + "read_only_depends_on": "eval:doc.is_master_data_imported==1" + }, + { + "default": "[]", + "fieldname": "failed_import_log", + "fieldtype": "Code", + "hidden": 1, + "options": "JSON" + }, + { + "fieldname": "failed_import_preview", + "fieldtype": "HTML", + "label": "Failed Import Log" + }, + { + "fieldname": "import_log_section", + "fieldtype": "Section Break", + "label": "Import Log" + }, + { + "fieldname": "default_round_off_account", + "fieldtype": "Link", + "label": "Default Round Off Account", + "options": "Account" + }, + { + "default": "[]", + "fieldname": "fixed_errors_log", + "fieldtype": "Code", + "hidden": 1, + "options": "JSON" + }, + { + "fieldname": "fixed_error_log_preview", + "fieldtype": "HTML", + "label": "Fixed Error Log" } ], "links": [], - "modified": "2020-04-16 13:03:28.894919", + "modified": "2020-04-28 00:29:18.039826", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 13474e19ee..462685f5e7 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import json import re +import sys import traceback import zipfile from decimal import Decimal @@ -15,18 +16,34 @@ from bs4 import BeautifulSoup as bs import frappe from erpnext import encode_company_abbr from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts +from erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer import unset_existing_data + from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.model.document import Document from frappe.model.naming import getseries, revert_series_if_last from frappe.utils.data import format_datetime - PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 +@frappe.whitelist() +def new_doc(document): + document = json.loads(document) + doctype = document.pop("doctype") + document.pop("name", None) + doc = frappe.new_doc(doctype) + doc.update(document) + + return doc + class TallyMigration(Document): + def validate(self): + failed_import_log = json.loads(self.failed_import_log) + sorted_failed_import_log = sorted(failed_import_log, key=lambda row: row["doc"]["creation"]) + self.failed_import_log = json.dumps(sorted_failed_import_log) + def autoname(self): if not self.name: self.name = "Tally Migration on " + format_datetime(self.creation) @@ -65,9 +82,17 @@ class TallyMigration(Document): "attached_to_name": self.name, "content": json.dumps(value), "is_private": True - }).insert() + }) + try: + f.insert() + except frappe.DuplicateEntryError: + pass setattr(self, key, f.file_url) + def set_account_defaults(self): + self.default_cost_center, self.default_round_off_account = frappe.db.get_value("Company", self.erpnext_company, ["cost_center", "round_off_account"]) + self.default_warehouse = frappe.db.get_value("Stock Settings", "Stock Settings", "default_warehouse") + def _process_master_data(self): def get_company_name(collection): return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip() @@ -84,7 +109,11 @@ class TallyMigration(Document): children, parents = get_children_and_parent_dict(accounts) group_set = [acc[1] for acc in accounts if acc[2]] children, customers, suppliers = remove_parties(parents, children, group_set) - coa = traverse({}, children, roots, roots, group_set) + + try: + coa = traverse({}, children, roots, roots, group_set) + except RecursionError: + self.log(_("Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name")) for account in coa: coa[account]["root_type"] = root_type_map[account] @@ -126,14 +155,18 @@ class TallyMigration(Document): def remove_parties(parents, children, group_set): customers, suppliers = set(), set() for account in parents: + found = False if self.tally_creditors_account in parents[account]: - children.pop(account, None) + found = True if account not in group_set: suppliers.add(account) - elif self.tally_debtors_account in parents[account]: - children.pop(account, None) + if self.tally_debtors_account in parents[account]: + found = True if account not in group_set: customers.add(account) + if found: + children.pop(account, None) + return children, customers, suppliers def traverse(tree, children, accounts, roots, group_set): @@ -151,6 +184,7 @@ class TallyMigration(Document): parties, addresses = [], [] for account in collection.find_all("LEDGER"): party_type = None + links = [] if account.NAME.string.strip() in customers: party_type = "Customer" parties.append({ @@ -161,7 +195,9 @@ class TallyMigration(Document): "territory": "All Territories", "customer_type": "Individual", }) - elif account.NAME.string.strip() in suppliers: + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + + if account.NAME.string.strip() in suppliers: party_type = "Supplier" parties.append({ "doctype": party_type, @@ -170,6 +206,8 @@ class TallyMigration(Document): "supplier_group": "All Supplier Groups", "supplier_type": "Individual", }) + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + if party_type: address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")]) addresses.append({ @@ -183,7 +221,7 @@ class TallyMigration(Document): "mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None, - "links": [{"link_doctype": party_type, "link_name": account["NAME"]}], + "links": links }) return parties, addresses @@ -242,12 +280,18 @@ class TallyMigration(Document): def create_company_and_coa(coa_file_url): coa_file = frappe.get_doc("File", {"file_url": coa_file_url}) frappe.local.flags.ignore_chart_of_accounts = True - company = frappe.get_doc({ - "doctype": "Company", - "company_name": self.erpnext_company, - "default_currency": "INR", - "enable_perpetual_inventory": 0, - }).insert() + + try: + company = frappe.get_doc({ + "doctype": "Company", + "company_name": self.erpnext_company, + "default_currency": "INR", + "enable_perpetual_inventory": 0, + }).insert() + except frappe.DuplicateEntryError: + company = frappe.get_doc("Company", self.erpnext_company) + unset_existing_data(self.erpnext_company) + frappe.local.flags.ignore_chart_of_accounts = False create_charts(company.name, custom_chart=json.loads(coa_file.get_content())) company.create_default_warehouses() @@ -256,36 +300,35 @@ class TallyMigration(Document): parties_file = frappe.get_doc("File", {"file_url": parties_file_url}) for party in json.loads(parties_file.get_content()): try: - frappe.get_doc(party).insert() + party_doc = frappe.get_doc(party) + party_doc.insert() except: - self.log(party) + self.log(party_doc) addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url}) for address in json.loads(addresses_file.get_content()): try: - frappe.get_doc(address).insert(ignore_mandatory=True) + address_doc = frappe.get_doc(address) + address_doc.insert(ignore_mandatory=True) except: - try: - gstin = address.pop("gstin", None) - frappe.get_doc(address).insert(ignore_mandatory=True) - self.log({"address": address, "message": "Invalid GSTIN: {}. Address was created without GSTIN".format(gstin)}) - except: - self.log(address) + self.log(address_doc) def create_items_uoms(items_file_url, uoms_file_url): uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url}) for uom in json.loads(uoms_file.get_content()): if not frappe.db.exists(uom): try: - frappe.get_doc(uom).insert() + uom_doc = frappe.get_doc(uom) + uom_doc.insert() except: - self.log(uom) + self.log(uom_doc) items_file = frappe.get_doc("File", {"file_url": items_file_url}) for item in json.loads(items_file.get_content()): try: - frappe.get_doc(item).insert() + item_doc = frappe.get_doc(item) + item_doc.insert() except: - self.log(item) + self.log(item_doc) try: self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) @@ -299,10 +342,13 @@ class TallyMigration(Document): self.publish("Import Master Data", _("Done"), 4, 4) + self.set_account_defaults() self.is_master_data_imported = 1 + frappe.db.commit() except: self.publish("Import Master Data", _("Process Failed"), -1, 5) + frappe.db.rollback() self.log() finally: @@ -323,7 +369,9 @@ class TallyMigration(Document): processed_voucher = function(voucher) if processed_voucher: vouchers.append(processed_voucher) + frappe.db.commit() except: + frappe.db.rollback() self.log(voucher) return vouchers @@ -349,6 +397,7 @@ class TallyMigration(Document): journal_entry = { "doctype": "Journal Entry", "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "company": self.erpnext_company, "accounts": accounts, @@ -377,6 +426,7 @@ class TallyMigration(Document): "doctype": doctype, party_field: voucher.PARTYNAME.string.strip(), "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "due_date": voucher.DATE.string.strip(), "items": get_voucher_items(voucher, doctype), @@ -468,14 +518,21 @@ class TallyMigration(Document): oldest_year = new_year def create_custom_fields(doctypes): - for doctype in doctypes: - df = { - "fieldtype": "Data", - "fieldname": "tally_guid", - "read_only": 1, - "label": "Tally GUID" - } - create_custom_field(doctype, df) + tally_guid_df = { + "fieldtype": "Data", + "fieldname": "tally_guid", + "read_only": 1, + "label": "Tally GUID" + } + tally_voucher_no_df = { + "fieldtype": "Data", + "fieldname": "tally_voucher_no", + "read_only": 1, + "label": "Tally Voucher Number" + } + for df in [tally_guid_df, tally_voucher_no_df]: + for doctype in doctypes: + create_custom_field(doctype, df) def create_price_list(): frappe.get_doc({ @@ -490,7 +547,7 @@ class TallyMigration(Document): try: frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") - frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) + frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.default_round_off_account) vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) vouchers = json.loads(vouchers_file.get_content()) @@ -521,11 +578,14 @@ class TallyMigration(Document): for index, voucher in enumerate(chunk, start=start): try: - doc = frappe.get_doc(voucher).insert() - doc.submit() + voucher_doc = frappe.get_doc(voucher) + voucher_doc.insert() + voucher_doc.submit() self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) + frappe.db.commit() except: - self.log(voucher) + frappe.db.rollback() + self.log(voucher_doc) if is_last: self.status = "" @@ -551,9 +611,22 @@ class TallyMigration(Document): frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600) def log(self, data=None): - data = data or self.status - message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) - return frappe.log_error(title="Tally Migration Error", message=message) + if isinstance(data, frappe.model.document.Document): + if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: + failed_import_log = json.loads(self.failed_import_log) + doc = data.as_dict() + failed_import_log.append({ + "doc": doc, + "exc": traceback.format_exc() + }) + self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':')) + self.save() + frappe.db.commit() + + else: + data = data or self.status + message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) + return frappe.log_error(title="Tally Migration Error", message=message) def set_status(self, status=""): self.status = status diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py index 0c13b6af9d..3dc7c1ec39 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py @@ -70,6 +70,7 @@ def validate_service_item(item, msg): if frappe.db.get_value('Item', item, 'is_stock_item'): frappe.throw(_(msg)) +@frappe.whitelist() def get_practitioner_list(doctype, txt, searchfield, start, page_len, filters=None): fields = ['name', 'practitioner_name', 'mobile_phone'] diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index 4c2d3f692a..2bef5fb5bd 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -15,6 +15,7 @@ class TestInpatientRecord(unittest.TestCase): patient = create_patient() # Schedule Admission ip_record = create_inpatient(patient) + ip_record.expected_length_of_stay = 0 ip_record.save(ignore_permissions = True) self.assertEqual(ip_record.name, frappe.db.get_value("Patient", patient, "inpatient_record")) self.assertEqual(ip_record.status, frappe.db.get_value("Patient", patient, "inpatient_status")) @@ -26,7 +27,7 @@ class TestInpatientRecord(unittest.TestCase): self.assertEqual("Occupied", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status")) # Discharge - schedule_discharge(patient=patient) + schedule_discharge(frappe.as_json({'patient': patient})) self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status")) ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name) @@ -44,8 +45,10 @@ class TestInpatientRecord(unittest.TestCase): patient = create_patient() ip_record = create_inpatient(patient) + ip_record.expected_length_of_stay = 0 ip_record.save(ignore_permissions = True) ip_record_new = create_inpatient(patient) + ip_record_new.expected_length_of_stay = 0 self.assertRaises(frappe.ValidationError, ip_record_new.save) service_unit = get_healthcare_service_unit() diff --git a/erpnext/hooks.py b/erpnext/hooks.py index ab161aa9f5..2a695896ed 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -41,7 +41,7 @@ boot_session = "erpnext.startup.boot.boot_session" notification_config = "erpnext.startup.notifications.get_notification_config" get_help_messages = "erpnext.utilities.activation.get_help_messages" leaderboards = "erpnext.startup.leaderboard.get_leaderboards" - +filters_config = "erpnext.startup.filters.get_filters_config" on_session_creation = [ "erpnext.portal.utils.create_customer_or_supplier", @@ -238,6 +238,9 @@ doc_events = { "on_cancel": "erpnext.regional.italy.utils.sales_invoice_on_cancel", "on_trash": "erpnext.regional.check_deletion_permission" }, + "Purchase Invoice": { + "on_submit": "erpnext.regional.india.utils.make_reverse_charge_entries" + }, "Payment Entry": { "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status"], "on_trash": "erpnext.regional.check_deletion_permission" @@ -320,8 +323,7 @@ scheduler_events = { "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_term_loans" ], "monthly_long": [ - "erpnext.accounts.deferred_revenue.convert_deferred_revenue_to_income", - "erpnext.accounts.deferred_revenue.convert_deferred_expense_to_expense", + "erpnext.accounts.deferred_revenue.process_deferred_accounting", "erpnext.hr.utils.allocate_earned_leaves", "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_demand_loans" ] diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py index 004477c26c..6d8091be64 100644 --- a/erpnext/hr/dashboard_fixtures.py +++ b/erpnext/hr/dashboard_fixtures.py @@ -24,24 +24,19 @@ def get_human_resource_dashboard(): "dashboard_name": "Human Resource", "is_default": 1, "charts": [ - { "chart": "Outgoing Salary", "width": "Full"}, + { "chart": "Attendance Count", "width": "Full"}, { "chart": "Gender Diversity Ratio", "width": "Half"}, { "chart": "Job Application Status", "width": "Half"}, { "chart": 'Designation Wise Employee Count', "width": "Half"}, { "chart": 'Department Wise Employee Count', "width": "Half"}, { "chart": 'Designation Wise Openings', "width": "Half"}, - { "chart": 'Department Wise Openings', "width": "Half"}, - { "chart": "Attendance Count", "width": "Full"} + { "chart": 'Department Wise Openings', "width": "Half"} ], "cards": [ {"card": "Total Employees"}, {"card": "New Joinees (Last year)"}, {'card': "Employees Left (Last year)"}, - {'card': "Total Job Openings (Last month)"}, {'card': "Total Applicants (Last month)"}, - {'card': "Shortlisted Candidates (Last month)"}, - {'card': "Rejected Candidates (Last month)"}, - {'card': "Total Job Offered (Last month)"}, ] } @@ -71,13 +66,6 @@ def get_charts(): filters_json = json.dumps([["Job Applicant", "creation", "Previous", "1 month"]])) ) - dashboard_charts.append( - get_dashboards_chart_doc('Outgoing Salary', "Sum", "Line", - document_type = "Salary Slip", based_on="end_date", - value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1, - filters_json = json.dumps([["Salary Slip", "docstatus", "=", 1]])) - ) - custom_options = '''{ "type": "line", "axisOptions": { @@ -143,7 +131,7 @@ def get_number_cards(): number_cards.append( get_number_cards_doc("Employee", "Employees Left (Last year)", filters_json = json.dumps([ - ["Employee", "modified", "Previous", "1 year"], + ["Employee", "relieving_date", "Previous", "1 year"], ["Employee", "status", "=", "Left"] ]) ) @@ -156,32 +144,6 @@ def get_number_cards(): ) ) - number_cards.append( - get_number_cards_doc("Job Opening", "Total Job Openings (Last month)", func = "Sum", - aggregate_function_based_on = "planned_vacancies", - filters_json = json.dumps([["Job Opening", "creation", "Previous", "1 month"]]) - ) - ) - number_cards.append( - get_number_cards_doc("Job Applicant", "Shortlisted Candidates (Last month)", filters_json = json.dumps([ - ["Job Applicant", "status", "=", "Accepted"], - ["Job Applicant", "creation", "Previous", "1 month"] - ]) - ) - ) - number_cards.append( - get_number_cards_doc("Job Applicant", "Rejected Candidates (Last month)", filters_json = json.dumps([ - ["Job Applicant", "status", "=", "Rejected"], - ["Job Applicant", "creation", "Previous", "1 month"] - ]) - ) - ) - number_cards.append( - get_number_cards_doc("Job Offer", "Total Job Offered (Last month)", - filters_json = json.dumps([["Job Offer", "creation", "Previous", "1 month"]]) - ) - ) - return number_cards diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 89abca00aa..0fed8d322f 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -18,12 +18,7 @@ { "hidden": 0, "label": "Leaves", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Payroll", - "links": "[\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Structure\",\n \"Employee\"\n ],\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -50,11 +45,6 @@ "label": "Recruitment", "links": "[\n {\n \"label\": \"Job Opening\",\n \"name\": \"Job Opening\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Applicant\",\n \"name\": \"Job Applicant\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Offer\",\n \"name\": \"Job Offer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Staffing Plan\",\n \"name\": \"Staffing Plan\",\n \"type\": \"doctype\"\n }\n]" }, - { - "hidden": 0, - "label": "Loans", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n }\n]" - }, { "hidden": 0, "label": "Training", @@ -69,18 +59,13 @@ "hidden": 0, "label": "Performance", "links": "[\n {\n \"label\": \"Appraisal\",\n \"name\": \"Appraisal\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Appraisal Template\",\n \"name\": \"Appraisal Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Rule\",\n \"name\": \"Energy Point Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Log\",\n \"name\": \"Energy Point Log\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Employee Tax and Benefits", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\",\n \"Payroll Period\"\n ],\n \"label\": \"Employee Other Income\",\n \"name\": \"Employee Other Income\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Modules", "charts": [ { - "chart_name": "Outgoing Salary", - "label": "Outgoing Salary" + "chart_name": "Attendance Count", + "label": "Attendance Count" } ], "creation": "2020-03-02 15:48:58.322521", @@ -93,7 +78,7 @@ "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-05-28 13:36:07.710600", + "modified": "2020-06-16 19:20:50.976045", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -103,21 +88,13 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#cef6d1", + "color": "#9deca2", "format": "{} Active", "label": "Employee", "link_to": "Employee", "stats_filter": "{\"status\":\"Active\"}", "type": "DocType" }, - { - "color": "#ffe8cd", - "format": "{} Open", - "label": "Leave Application", - "link_to": "Leave Application", - "stats_filter": "{\"status\":\"Open\"}", - "type": "DocType" - }, { "label": "Attendance", "link_to": "Attendance", @@ -125,12 +102,19 @@ "type": "DocType" }, { - "label": "Salary Structure", - "link_to": "Payroll Entry", + "format": "{} Open", + "label": "Leave Application", + "link_to": "Leave Application", + "stats_filter": "{\"status\":\"Open\"}", "type": "DocType" }, { - "label": "Salary Register", + "label": "Job Applicant", + "link_to": "Job Applicant", + "type": "DocType" + }, + { + "label": "Monthly Attendance Sheet", "link_to": "Monthly Attendance Sheet", "type": "Report" }, diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json index 906f6f77f2..a656a7ea5f 100644 --- a/erpnext/hr/doctype/attendance/attendance.json +++ b/erpnext/hr/doctype/attendance/attendance.json @@ -19,11 +19,15 @@ "attendance_date", "company", "department", - "shift", "attendance_request", - "amended_from", + "details_section", + "shift", + "in_time", + "out_time", + "column_break_18", "late_entry", - "early_exit" + "early_exit", + "amended_from" ], "fields": [ { @@ -172,13 +176,36 @@ "fieldname": "early_exit", "fieldtype": "Check", "label": "Early Exit" + }, + { + "fieldname": "details_section", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "depends_on": "shift", + "fieldname": "in_time", + "fieldtype": "Datetime", + "label": "In Time", + "read_only": 1 + }, + { + "depends_on": "shift", + "fieldname": "out_time", + "fieldtype": "Datetime", + "label": "Out Time", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" } ], "icon": "fa fa-ok", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2020-04-11 11:40:14.319496", + "modified": "2020-05-29 13:51:37.177231", "modified_by": "Administrator", "module": "HR", "name": "Attendance", diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py index df0f75a18c..d4c118f802 100644 --- a/erpnext/hr/doctype/department_approver/department_approver.py +++ b/erpnext/hr/doctype/department_approver/department_approver.py @@ -19,7 +19,7 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): approvers = [] department_details = {} department_list = [] - employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver"], as_dict=True) + employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver", "expense_approver"], as_dict=True) employee_department = filters.get("department") or employee.department if employee_department: @@ -33,10 +33,16 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): if filters.get("doctype") == "Leave Application" and employee.leave_approver: approvers.append(frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name'])) + if filters.get("doctype") == "Expense Claim" and employee.expense_approver: + approvers.append(frappe.db.get_value("User", employee.expense_approver, ['name', 'first_name', 'last_name'])) + + if filters.get("doctype") == "Leave Application": parentfield = "leave_approvers" + field_name = "Leave Approver" else: parentfield = "expense_approvers" + field_name = "Expense Approver" if department_list: for d in department_list: approvers += frappe.db.sql("""select user.name, user.first_name, user.last_name from @@ -46,4 +52,12 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): and approver.parentfield = %s and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True) + if len(approvers) == 0: + frappe.throw(_("Please set {0} for the Employee or for Department: {1}"). + format( + field_name, frappe.bold(employee_department), + frappe.bold(employee.name) + ), + title=_(field_name + " Missing")) + return set(tuple(approver) for approver in approvers) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index f575765f69..7dacacf12b 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -62,6 +62,7 @@ "salary_mode", "payroll_cost_center", "column_break_52", + "expense_approver", "bank_name", "bank_ac_no", "health_insurance_section", @@ -205,7 +206,7 @@ "label": "Status", "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nActive\nLeft", + "options": "Active\nLeft", "reqd": 1, "search_index": 1 }, @@ -667,6 +668,7 @@ "oldfieldtype": "Date" }, { + "depends_on": "eval:doc.status == \"Left\"", "fieldname": "relieving_date", "fieldtype": "Date", "label": "Relieving Date", @@ -797,13 +799,21 @@ { "fieldname": "column_break_52", "fieldtype": "Column Break" + }, + { + "fieldname": "expense_approver", + "fieldtype": "Link", + "label": "Expense Approver", + "options": "User", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-user", "idx": 24, "image_field": "image", "links": [], - "modified": "2020-05-05 18:51:03.152503", + "modified": "2020-06-18 18:01:27.223535", "modified_by": "Administrator", "module": "HR", "name": "Employee", diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index 6cc49cfff2..cba8ee9a40 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -139,13 +139,13 @@ frappe.ui.form.on('Employee Advance', { employee: function (frm) { if (frm.doc.employee) { return frappe.call({ - method: "erpnext.hr.doctype.employee_advance.employee_advance.get_due_advance_amount", + method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount", args: { "employee": frm.doc.employee, "posting_date": frm.doc.posting_date }, callback: function(r) { - frm.set_value("due_advance_amount",r.message); + frm.set_value("pending_amount",r.message); } }); } diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json index 8c5ce42d87..0d90913871 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.json +++ b/erpnext/hr/doctype/employee_advance/employee_advance.json @@ -19,7 +19,7 @@ "column_break_11", "advance_amount", "paid_amount", - "due_advance_amount", + "pending_amount", "claimed_amount", "return_amount", "section_break_7", @@ -102,14 +102,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "depends_on": "eval:cur_frm.doc.employee", - "fieldname": "due_advance_amount", - "fieldtype": "Currency", - "label": "Due Advance Amount", - "options": "Company:company:default_currency", - "read_only": 1 - }, { "fieldname": "claimed_amount", "fieldtype": "Currency", @@ -177,11 +169,19 @@ "fieldname": "repay_unclaimed_amount_from_salary", "fieldtype": "Check", "label": "Repay unclaimed amount from salary" + }, + { + "depends_on": "eval:cur_frm.doc.employee", + "fieldname": "pending_amount", + "fieldtype": "Currency", + "label": "Pending Amount", + "options": "Company:company:default_currency", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-03-06 15:11:33.747535", + "modified": "2020-06-12 12:42:39.833818", "modified_by": "Administrator", "module": "HR", "name": "Employee Advance", diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index db39eff0e4..76195812c8 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -22,6 +22,7 @@ class EmployeeAdvance(Document): self.validate_employee_advance_account() def on_cancel(self): + self.ignore_linked_doctypes = ('GL Entry') self.set_status() def set_status(self): @@ -95,7 +96,7 @@ class EmployeeAdvance(Document): frappe.db.set_value("Employee Advance", self.name, "status", self.status) @frappe.whitelist() -def get_due_advance_amount(employee, posting_date): +def get_pending_amount(employee, posting_date): employee_due_amount = frappe.get_all("Employee Advance", \ filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \ fields = ["advance_amount", "paid_amount"]) diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json deleted file mode 100644 index cf624195b7..0000000000 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json +++ /dev/null @@ -1,576 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-BEN-APP-.YY.-.MM.-.#####", - "beta": 0, - "creation": "2018-04-13 16:31:39.190787", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "max_benefits", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Max Benefits (Yearly)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "remaining_benefit", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Remaining Benefits (Yearly)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payroll_period", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Payroll Period", - "length": 0, - "no_copy": 0, - "options": "Payroll Period", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Benefit Application", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Benefits Applied", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee_benefits", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Benefits", - "length": 0, - "no_copy": 0, - "options": "Employee Benefit Application Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "totals", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Totals", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pro_rata_dispensed_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Dispensed Amount (Pro-rated)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:39.714081", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Benefit Application", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json deleted file mode 100644 index 56421db8c3..0000000000 --- a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json +++ /dev/null @@ -1,177 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2018-04-13 16:36:18.389786", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "earning_component", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Earning Component", - "length": 0, - "no_copy": 0, - "options": "Salary Component", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "earning_component.pay_against_benefit_claim", - "fieldname": "pay_against_benefit_claim", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pay Against Benefit Claim", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "earning_component.max_benefit_amount", - "fieldname": "max_benefit_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Max Benefit Amount", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-08-21 16:15:42.111118", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Benefit Application Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json deleted file mode 100644 index 1aa69d02a5..0000000000 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json +++ /dev/null @@ -1,580 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "HR-BEN-CLM-.YY.-.MM.-.#####", - "beta": 0, - "creation": "2018-04-13 16:43:10.386409", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "claim_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Claim Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "benefit_type_and_amount", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Benefit Type and Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "earning_component", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Claim Benefit For", - "length": 0, - "no_copy": 0, - "options": "Salary Component", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "earning_component.max_benefit_amount", - "fieldname": "max_amount_eligible", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Max Amount Eligible", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "earning_component.pay_against_benefit_claim", - "fieldname": "pay_against_benefit_claim", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pay Against Benefit Claim", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "claimed_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Claimed Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "salary_slip", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salary Slip", - "length": 0, - "no_copy": 0, - "options": "Salary Slip", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Benefit Claim", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Expense Proof", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "attachments", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Attachments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:35.942067", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Benefit Claim", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index 86705121ac..15fbd4e015 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -72,7 +72,7 @@ def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=N return doc -def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, shift=None): +def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, in_time=None, out_time=None, shift=None): """Creates an attendance and links the attendance to the Employee Checkin. Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown. @@ -100,7 +100,9 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki 'company': employee_doc.company, 'shift': shift, 'late_entry': late_entry, - 'early_exit': early_exit + 'early_exit': early_exit, + 'in_time': in_time, + 'out_time': out_time } attendance = frappe.get_doc(doc_dict).insert() attendance.submit() diff --git a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py index 420bbe6b1a..9e7d3186b8 100644 --- a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py +++ b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils import getdate, add_days -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee class TestEmployeePromotion(unittest.TestCase): def setUp(self): diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json deleted file mode 100644 index 66fac5bee5..0000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json +++ /dev/null @@ -1,169 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-04-13 16:51:36.971140", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "max_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Max Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, - "fieldname": "is_active", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Active", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-25 13:20:31.367158", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Category", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json deleted file mode 100644 index 7b3b8f5caa..0000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 16:56:23.333041", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "exemption_sub_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Sub Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Sub Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_sub_category.exemption_category", - "fetch_if_empty": 0, - "fieldname": "exemption_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_sub_category.max_amount", - "fetch_if_empty": 0, - "fieldname": "max_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maximum Exempted Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Declared Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-04-26 11:28:14.023086", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Declaration Category", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py deleted file mode 100644 index e54d9193ba..0000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest -# from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_exemption_category, create_payroll_period -# -# class TestEmployeeTaxExemptionProofSubmission(unittest.TestCase): -# def setup(self): -# make_employee("employee@proofsubmission.com") -# create_payroll_period() -# create_exemption_category() -# frappe.db.sql("""delete from `tabEmployee Tax Exemption Proof Submission`""") -# -# def test_exemption_amount_lesser_than_category_max(self): -# declaration = frappe.get_doc({ -# "doctype": "Employee Tax Exemption Proof Submission", -# "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), -# "payroll_period": "Test Payroll Period", -# "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", -# type_of_proof = "Test Proof", -# exemption_category = "_Test Category", -# amount = 150000)] -# }) -# self.assertRaises(frappe.ValidationError, declaration.save) -# declaration = frappe.get_doc({ -# "doctype": "Employee Tax Exemption Proof Submission", -# "payroll_period": "Test Payroll Period", -# "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), -# "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", -# type_of_proof = "Test Proof", -# exemption_category = "_Test Category", -# amount = 100000)] -# }) -# self.assertTrue(declaration.save) -# self.assertTrue(declaration.submit) -# -# def test_duplicate_category_in_proof_submission(self): -# declaration = frappe.get_doc({ -# "doctype": "Employee Tax Exemption Proof Submission", -# "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), -# "payroll_period": "Test Payroll Period", -# "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", -# exemption_category = "_Test Category", -# type_of_proof = "Test Proof", -# amount = 100000), -# dict(exemption_sub_category = "_Test Sub Category", -# exemption_category = "_Test Category", -# amount = 50000), -# ] -# }) -# self.assertRaises(frappe.ValidationError, declaration.save) diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json deleted file mode 100644 index b9254afad0..0000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json +++ /dev/null @@ -1,213 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 17:19:03.006149", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "exemption_sub_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Sub Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Sub Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_sub_category.exemption_category", - "fetch_if_empty": 0, - "fieldname": "exemption_category", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Category", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_sub_category.max_amount", - "fetch_if_empty": 0, - "fieldname": "max_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maximum Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "type_of_proof", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Type of Proof", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Actual Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-04-25 15:45:03.154904", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Proof Submission Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json deleted file mode 100644 index b0e492e7ca..0000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-05-09 12:47:26.983095", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "exemption_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Tax Exemption Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_category.max_amount", - "fetch_if_empty": 1, - "fieldname": "max_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Max Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, - "fieldname": "is_active", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Active", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-25 13:24:05.164877", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Sub Category", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index fb2310396b..fa63ec2834 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -213,12 +213,15 @@ frappe.ui.form.on("Expense Claim", { refresh: function(frm) { frm.trigger("toggle_fields"); - if(frm.doc.docstatus === 1 && frm.doc.approval_status !== "Rejected") { + if(frm.doc.docstatus > 0 && frm.doc.approval_status !== "Rejected") { frm.add_custom_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: frm.doc.name, company: frm.doc.company, - group_by_voucher: false + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), + group_by: '', + show_cancelled_entries: frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __("View")); @@ -243,7 +246,6 @@ frappe.ui.form.on("Expense Claim", { }, update_employee_advance_claimed_amount: function(frm) { - console.log("update_employee_advance_claimed_amount") let amount_to_be_allocated = frm.doc.grand_total; $.each(frm.doc.advances || [], function(i, advance){ if (amount_to_be_allocated >= advance.unclaimed_amount){ @@ -295,6 +297,16 @@ frappe.ui.form.on("Expense Claim", { frm.events.get_advances(frm); }, + cost_center: function(frm) { + frm.events.set_child_cost_center(frm); + }, + set_child_cost_center: function(frm){ + (frm.doc.expenses || []).forEach(function(d) { + if (!d.cost_center){ + d.cost_center = frm.doc.cost_center; + } + }); + }, get_taxes: function(frm) { if(frm.doc.taxes) { frappe.call({ @@ -338,8 +350,7 @@ frappe.ui.form.on("Expense Claim", { frappe.ui.form.on("Expense Claim Detail", { expenses_add: function(frm, cdt, cdn) { - var row = frappe.get_doc(cdt, cdn); - frm.script_manager.copy_from_first_row("expenses", row, ["cost_center"]); + frm.events.set_child_cost_center(frm); }, amount: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 96baaab595..fa28470af8 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -66,6 +66,7 @@ "fieldname": "employee", "fieldtype": "Link", "in_global_search": 1, + "in_standard_filter": 1, "label": "From Employee", "oldfieldname": "employee", "oldfieldtype": "Link", @@ -164,6 +165,7 @@ "default": "Today", "fieldname": "posting_date", "fieldtype": "Date", + "in_standard_filter": 1, "label": "Posting Date", "oldfieldname": "posting_date", "oldfieldtype": "Date", @@ -236,6 +238,7 @@ { "fieldname": "company", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Company", "oldfieldname": "company", "oldfieldtype": "Link", @@ -368,7 +371,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2019-12-14 23:52:05.388458", + "modified": "2020-06-15 12:43:04.099803", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js index b629b42f9b..fd082fda09 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.js +++ b/erpnext/hr/doctype/hr_settings/hr_settings.js @@ -2,21 +2,6 @@ // For license information, please see license.txt frappe.ui.form.on('HR Settings', { - encrypt_salary_slips_in_emails: function(frm) { - let encrypt_state = frm.doc.encrypt_salary_slips_in_emails; - frm.set_df_property('password_policy', 'reqd', encrypt_state); - }, - - validate: function(frm) { - let policy = frm.doc.password_policy; - if (policy) { - if (policy.includes(' ') || policy.includes('--')) { - frappe.msgprint(__("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically")); - } - frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); - } - }, - restrict_backdated_leave_application: function(frm) { frm.toggle_reqd("role_allowed_to_create_backdated_leave_application", frm.doc.restrict_backdated_leave_application); } diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json index ebf8723be6..c42e1d72fc 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ b/erpnext/hr/doctype/hr_settings/hr_settings.json @@ -12,16 +12,6 @@ "column_break_4", "stop_birthday_reminders", "expense_approver_mandatory_in_expense_claim", - "payroll_settings", - "payroll_based_on", - "max_working_hours_against_timesheet", - "include_holidays_in_total_working_days", - "disable_rounded_total", - "column_break_11", - "daily_wages_fraction_for_half_day", - "email_salary_slip_to_employee", - "encrypt_salary_slips_in_emails", - "password_policy", "leave_settings", "leave_approval_notification_template", "leave_status_notification_template", @@ -38,13 +28,17 @@ { "fieldname": "employee_settings", "fieldtype": "Section Break", - "label": "Employee Settings" + "label": "Employee Settings", + "show_days": 1, + "show_seconds": 1 }, { "description": "Enter retirement age in years", "fieldname": "retirement_age", "fieldtype": "Data", - "label": "Retirement Age" + "label": "Retirement Age", + "show_days": 1, + "show_seconds": 1 }, { "default": "Naming Series", @@ -52,161 +46,126 @@ "fieldname": "emp_created_by", "fieldtype": "Select", "label": "Employee Records to be created by", - "options": "Naming Series\nEmployee Number\nFull Name" + "options": "Naming Series\nEmployee Number\nFull Name", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_4", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "description": "Don't send Employee Birthday Reminders", "fieldname": "stop_birthday_reminders", "fieldtype": "Check", - "label": "Stop Birthday Reminders" + "label": "Stop Birthday Reminders", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "expense_approver_mandatory_in_expense_claim", "fieldtype": "Check", - "label": "Expense Approver Mandatory In Expense Claim" - }, - { - "fieldname": "payroll_settings", - "fieldtype": "Section Break", - "label": "Payroll Settings" - }, - { - "default": "0", - "description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day", - "fieldname": "include_holidays_in_total_working_days", - "fieldtype": "Check", - "label": "Include holidays in Total no. of Working Days" - }, - { - "fieldname": "max_working_hours_against_timesheet", - "fieldtype": "Float", - "label": "Max working hours against Timesheet" - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "default": "1", - "description": "Emails salary slip to employee based on preferred email selected in Employee", - "fieldname": "email_salary_slip_to_employee", - "fieldtype": "Check", - "label": "Email Salary Slip to Employee" - }, - { - "default": "0", - "depends_on": "eval: doc.email_salary_slip_to_employee == 1;", - "description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.", - "fieldname": "encrypt_salary_slips_in_emails", - "fieldtype": "Check", - "label": "Encrypt Salary Slips in Emails" - }, - { - "depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1", - "description": "Example: SAL-{first_name}-{date_of_birth.year}
This will generate a password like SAL-Jane-1972", - "fieldname": "password_policy", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Password Policy" + "label": "Expense Approver Mandatory In Expense Claim", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "leave_settings", "fieldtype": "Section Break", - "label": "Leave Settings" + "label": "Leave Settings", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "leave_approval_notification_template", "fieldtype": "Link", "label": "Leave Approval Notification Template", - "options": "Email Template" + "options": "Email Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "leave_status_notification_template", "fieldtype": "Link", "label": "Leave Status Notification Template", - "options": "Email Template" + "options": "Email Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_18", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "leave_approver_mandatory_in_leave_application", "fieldtype": "Check", - "label": "Leave Approver Mandatory In Leave Application" + "label": "Leave Approver Mandatory In Leave Application", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "show_leaves_of_all_department_members_in_calendar", "fieldtype": "Check", - "label": "Show Leaves Of All Department Members In Calendar" + "label": "Show Leaves Of All Department Members In Calendar", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "hiring_settings", "fieldtype": "Section Break", - "label": "Hiring Settings" + "label": "Hiring Settings", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "check_vacancies", "fieldtype": "Check", - "label": "Check Vacancies On Job Offer Creation" + "label": "Check Vacancies On Job Offer Creation", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "auto_leave_encashment", "fieldtype": "Check", - "label": "Auto Leave Encashment" - }, - { - "default": "0", - "description": "If checked, hides and disables Rounded Total field in Salary Slips", - "fieldname": "disable_rounded_total", - "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Auto Leave Encashment", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "restrict_backdated_leave_application", "fieldtype": "Check", - "label": "Restrict Backdated Leave Application" + "label": "Restrict Backdated Leave Application", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.restrict_backdated_leave_application == 1", "fieldname": "role_allowed_to_create_backdated_leave_application", "fieldtype": "Link", "label": "Role Allowed to Create Backdated Leave Application", - "options": "Role" - }, - { - "default": "Leave", - "fieldname": "payroll_based_on", - "fieldtype": "Select", - "label": "Calculate Payroll Working Days Based On", - "options": "Leave\nAttendance" - }, - { - "default": "0.5", - "description": "The fraction of daily wages to be paid for half-day attendance", - "fieldname": "daily_wages_fraction_for_half_day", - "fieldtype": "Float", - "label": "Daily Wages Fraction for Half Day" + "options": "Role", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2020-05-11 13:02:51.274347", + "modified": "2020-06-04 15:15:09.865476", "modified_by": "Administrator", "module": "HR", "name": "HR Settings", diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.py b/erpnext/hr/doctype/hr_settings/hr_settings.py index 5ed4c87c62..ced98fb9a5 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.py +++ b/erpnext/hr/doctype/hr_settings/hr_settings.py @@ -5,34 +5,14 @@ from __future__ import unicode_literals import frappe -from frappe import _ from frappe.model.document import Document -from frappe.utils import cint -from frappe.custom.doctype.property_setter.property_setter import make_property_setter class HRSettings(Document): def validate(self): self.set_naming_series() - self.validate_password_policy() - - if not self.daily_wages_fraction_for_half_day: - self.daily_wages_fraction_for_half_day = 0.5 def set_naming_series(self): from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series set_by_naming_series("Employee", "employee_number", self.get("emp_created_by")=="Naming Series", hide_name_field=True) - def validate_password_policy(self): - if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails: - if not self.password_policy: - frappe.throw(_("Password policy for Salary Slips is not set")) - - def on_update(self): - self.toggle_rounded_total() - frappe.clear_cache() - - def toggle_rounded_total(self): - self.disable_rounded_total = cint(self.disable_rounded_total) - make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check") - make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check") diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py index 32f1b89f49..f9ee44a4de 100644 --- a/erpnext/hr/doctype/job_offer/job_offer.py +++ b/erpnext/hr/doctype/job_offer/job_offer.py @@ -20,10 +20,9 @@ class JobOffer(Document): staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date) check_vacancies = frappe.get_single("HR Settings").check_vacancies if staffing_plan and check_vacancies: - vacancies = frappe.db.get_value("Staffing Plan Detail", filters={"name": staffing_plan.name}, fieldname=['vacancies']) - job_offers = len(self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)) - if vacancies - job_offers <= 0: - frappe.throw(_("There are no vacancies under staffing plan {0}").format(get_link_to_form("Staffing Plan", staffing_plan.parent))) + job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date) + if staffing_plan.vacancies - len(job_offers) <= 0: + frappe.throw(_("There are no vacancies under staffing plan {0}").format(frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent)))) def on_change(self): update_job_applicant(self.status, self.job_applicant) @@ -33,7 +32,8 @@ class JobOffer(Document): return frappe.get_all("Job Offer", filters={ "offer_date": ['between', (from_date, to_date)], "designation": self.designation, - "company": self.company + "company": self.company, + "docstatus": 1 }, fields=['name']) def update_job_applicant(status, job_applicant): @@ -42,18 +42,22 @@ def update_job_applicant(status, job_applicant): def get_staffing_plan_detail(designation, company, offer_date): detail = frappe.db.sql(""" - SELECT spd.name as name, + SELECT DISTINCT spd.parent, sp.from_date as from_date, sp.to_date as to_date, - sp.name as parent + sp.name, + sum(spd.vacancies) as vacancies, + spd.designation FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp WHERE sp.docstatus=1 AND spd.designation=%s AND sp.company=%s + AND spd.parent = sp.name AND %s between sp.from_date and sp.to_date """, (designation, company, offer_date), as_dict=1) - return detail[0] if detail else None + + return frappe._dict(detail[0]) if detail else None @frappe.whitelist() def make_employee(source_name, target_doc=None): diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 1f50e27098..fb1f2c00b1 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -38,11 +38,15 @@ frappe.ui.form.on("Leave Application", { }, validate: function(frm) { + if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){ + frm.doc.half_day_date = frm.doc.from_date; + } frm.toggle_reqd("half_day_date", frm.doc.half_day == 1); }, make_dashboard: function(frm) { var leave_details; + let lwps; if (frm.doc.employee) { frappe.call({ method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details", @@ -58,6 +62,7 @@ frappe.ui.form.on("Leave Application", { if (!r.exc && r.message['leave_approver']) { frm.set_value('leave_approver', r.message['leave_approver']); } + lwps = r.message["lwps"]; } }); $("div").remove(".form-dashboard-section.custom"); @@ -67,6 +72,18 @@ frappe.ui.form.on("Leave Application", { }) ); frm.dashboard.show(); + let allowed_leave_types = Object.keys(leave_details); + + // lwps should be allowed, lwps don't have any allocation + allowed_leave_types = allowed_leave_types.concat(lwps); + + frm.set_query('leave_type', function(){ + return { + filters : [ + ['leave_type_name', 'in', allowed_leave_types] + ] + }; + }); } }, diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 84f2c836cc..0423824c0e 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -19,7 +19,6 @@ class NotAnOptionalHoliday(frappe.ValidationError): pass from frappe.model.document import Document class LeaveApplication(Document): - def get_feed(self): return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type) @@ -33,6 +32,7 @@ class LeaveApplication(Document): self.validate_block_days() self.validate_salary_processed_days() self.validate_attendance() + self.set_half_day_date() if frappe.db.get_value("Leave Type", self.leave_type, 'is_optional_leave'): self.validate_optional_leave() self.validate_applicable_after() @@ -131,8 +131,6 @@ class LeaveApplication(Document): for dt in daterange(getdate(self.from_date), getdate(self.to_date)): date = dt.strftime("%Y-%m-%d") status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave" - print("-------->>>", status) - # frappe.throw("Hello") attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_date = date, docstatus = ('!=', 2))) @@ -292,6 +290,10 @@ class LeaveApplication(Document): frappe.throw(_("{0} is not in Optional Holiday List").format(formatdate(day)), NotAnOptionalHoliday) day = add_days(day, 1) + def set_half_day_date(self): + if self.from_date == self.to_date and self.half_day == 1: + self.half_day_date = self.from_date + def notify_employee(self): employee = frappe.get_doc("Employee", self.employee) if not employee.user_id: @@ -438,21 +440,36 @@ def get_leave_details(employee, date): leave_allocation = {} for d in allocation_records: allocation = allocation_records.get(d, frappe._dict()) + + total_allocated_leaves = frappe.db.get_value('Leave Allocation', { + 'from_date': ('<=', date), + 'to_date': ('>=', date), + 'employee': employee, + 'leave_type': allocation.leave_type, + }, 'SUM(total_leaves_allocated)') or 0 + remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date, consider_all_leaves_in_the_allocation_period=True) + end_date = allocation.to_date leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1 leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, end_date) leave_allocation[d] = { - "total_leaves": allocation.total_leaves_allocated, + "total_leaves": total_allocated_leaves, + "expired_leaves": total_allocated_leaves - (remaining_leaves + leaves_taken), "leaves_taken": leaves_taken, "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} + #is used in set query + lwps = frappe.get_list("Leave Type", filters = {"is_lwp": 1}) + lwps = [lwp.name for lwp in lwps] + ret = { 'leave_allocation': leave_allocation, - 'leave_approver': get_leave_approver(employee) + 'leave_approver': get_leave_approver(employee), + 'lwps': lwps } return ret diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index 2385b6ac1c..d30e3b9f9c 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -1,22 +1,23 @@ -{% if data %} +{% if not jQuery.isEmptyObject(data) %}
{{ __("Allocated Leaves") }}
- - - - - + + + + + + - {% for(const [key, value] of Object.entries(data)) { %} + @@ -24,6 +25,6 @@ {% } %}
{{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}{{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Expired Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}
{%= key %} {%= value["total_leaves"] %} {%= value["expired_leaves"] %} {%= value["leaves_taken"] %} {%= value["pending_leaves"] %} {%= value["remaining_leaves"] %}
-{% } else { %} +{% else %}

No Leaves have been allocated.

-{% } %} \ No newline at end of file +{% endif %} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 50a08b12bc..8913c648c5 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -8,7 +8,7 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate, nowdate, flt from erpnext.hr.utils import set_employee_name -from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure +from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py index ac7755b23a..99f6463416 100644 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py @@ -7,7 +7,7 @@ import frappe import unittest from frappe.utils import today, add_months from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy\ diff --git a/erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.json deleted file mode 100644 index 0dd3403d66..0000000000 --- a/erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.json +++ /dev/null @@ -1,209 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-30 06:07:33.477781", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Designation", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-30 11:28:16.544471", - "modified_by": "Administrator", - "module": "HR", - "name": "Payroll Employee Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/payroll_period_date/payroll_period_date.json b/erpnext/hr/doctype/payroll_period_date/payroll_period_date.json deleted file mode 100644 index 29bd2a3322..0000000000 --- a/erpnext/hr/doctype/payroll_period_date/payroll_period_date.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 15:17:30.513630", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "end_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-04-13 19:39:37.473294", - "modified_by": "Administrator", - "module": "HR", - "name": "Payroll Period Date", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json deleted file mode 100644 index 797f8f7c02..0000000000 --- a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-06-14 19:22:29.811658", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "time_sheet", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Time Sheet", - "length": 0, - "no_copy": 0, - "options": "Timesheet", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "working_hours", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Working Hours", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-02-19 08:33:41.762144", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Slip Timesheet", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py index d56080eecd..19735648aa 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ b/erpnext/hr/doctype/shift_type/shift_type.py @@ -28,13 +28,14 @@ class ShiftType(Document): logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time") for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])): single_shift_logs = list(group) - attendance_status, working_hours, late_entry, early_exit = self.get_attendance(single_shift_logs) - mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, self.name) + attendance_status, working_hours, late_entry, early_exit, in_time, out_time = self.get_attendance(single_shift_logs) + mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, in_time, out_time, self.name) for employee in self.get_assigned_employee(self.process_attendance_after, True): self.mark_absent_for_dates_with_no_attendance(employee) def get_attendance(self, logs): - """Return attendance_status, working_hours for a set of logs belonging to a single shift. + """Return attendance_status, working_hours, late_entry, early_exit, in_time, out_time + for a set of logs belonging to a single shift. Assumtion: 1. These logs belongs to an single shift, single employee and is not in a holiday date. 2. Logs are in chronological order @@ -48,10 +49,10 @@ class ShiftType(Document): early_exit = True if self.working_hours_threshold_for_absent and total_working_hours < self.working_hours_threshold_for_absent: - return 'Absent', total_working_hours, late_entry, early_exit + return 'Absent', total_working_hours, late_entry, early_exit, in_time, out_time if self.working_hours_threshold_for_half_day and total_working_hours < self.working_hours_threshold_for_half_day: - return 'Half Day', total_working_hours, late_entry, early_exit - return 'Present', total_working_hours, late_entry, early_exit + return 'Half Day', total_working_hours, late_entry, early_exit, in_time, out_time + return 'Present', total_working_hours, late_entry, early_exit, in_time, out_time def mark_absent_for_dates_with_no_attendance(self, employee): """Marks Absents for the given employee on working days in this shift which have no attendance marked. diff --git a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json deleted file mode 100644 index a094f8a197..0000000000 --- a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json +++ /dev/null @@ -1,232 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 17:42:13.516032", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "From Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "to_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "To Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "percent_deduction", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Percent Deduction", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "condition", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Condition", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "html_6", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "

Condition Examples

\n
    \n
  1. Applying tax if employee born between 31-12-1937 and 01-01-1958 (Employees aged 60 to 80)
    \nCondition: date_of_birth>date(1937, 12, 31) and date_of_birth<date(1958, 01, 01)

  2. Applying tax by employee gender
    \nCondition: gender==\"Male\"

  3. \n
  4. Applying tax by Salary Component
    \nCondition: base > 10000
", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-06-19 10:10:23.732132", - "modified_by": "Administrator", - "module": "HR", - "name": "Taxable Salary Slab", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py index 57123e304f..313f90eba8 100644 --- a/erpnext/hr/doctype/training_event/test_training_event.py +++ b/erpnext/hr/doctype/training_event/test_training_event.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils import today, add_days -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee class TestTrainingEvent(unittest.TestCase): def setUp(self): diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py index 61faea1871..edf05e827b 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py @@ -22,6 +22,9 @@ def get_template(): args = frappe.local.form_dict + if getdate(args.from_date) > getdate(args.to_date): + frappe.throw(_("To Date should be greater than From Date")) + w = UnicodeWriter() w = add_header(w) diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json index 208e394560..32472b4b3f 100644 --- a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json +++ b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-27 11:47:34.700174", + "creation": "2020-05-28 11:47:34.700174", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -14,6 +14,6 @@ "owner": "Administrator", "reference_document": "Holiday List", "show_full_form": 1, - "title": "Create Holiday list", + "title": "Create Holiday List", "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json index a8c96fb682..0a1d0baf8a 100644 --- a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json +++ b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json @@ -1,6 +1,6 @@ { "action": "Update Settings", - "creation": "2020-05-14 13:13:52.427711", + "creation": "2020-05-28 13:13:52.427711", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -14,6 +14,6 @@ "owner": "Administrator", "reference_document": "HR Settings", "show_full_form": 0, - "title": "HR settings", + "title": "HR Settings", "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py index df64006c1b..8f39388926 100644 --- a/erpnext/hr/report/employee_analytics/employee_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -26,7 +26,7 @@ def get_columns(): return [ _("Employee") + ":Link/Employee:120", _("Name") + ":Data:200", _("Date of Birth")+ ":Date:100", _("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120", - _("Designation") + ":Link/Designation:120", _("Gender") + "::60", _("Company") + ":Link/Company:120" + _("Designation") + ":Link/Designation:120", _("Gender") + "::100", _("Company") + ":Link/Company:120" ] def get_conditions(filters): @@ -43,7 +43,12 @@ def get_employees(filters): gender, company from `tabEmployee` where status = 'Active' %s""" % conditions, as_list=1) def get_parameters(filters): - return frappe.db.sql("""select name from `tab"""+filters.get("parameter")+"""` """, as_list=1) + if filters.get("parameter") == "Grade": + parameter = "Employee Grade" + else: + parameter = filters.get("parameter") + + return frappe.db.sql("""select name from `tab"""+ parameter +"""` """, as_list=1) def get_chart_data(parameters,employees, filters): if not parameters: diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 97be5cd813..db1d191758 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -3,13 +3,13 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt +from frappe.utils import flt, add_days from frappe import _ -from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on, get_leave_allocation_records +from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on def execute(filters=None): if filters.to_date <= filters.from_date: - frappe.throw(_('From date can not be greater than than To date')) + frappe.throw(_('"From date" can not be greater than or equal to "To date"')) columns = get_columns() data = get_data(filters) @@ -104,14 +104,17 @@ def get_data(filters): new_allocation, expired_leaves = get_allocated_and_expired_leaves(filters.from_date, filters.to_date, employee.name, leave_type) - opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) - closing = get_leave_balance_on(employee.name, leave_type, filters.to_date) + opening = get_leave_balance_on(employee.name, leave_type, add_days(filters.from_date, -1)) #allocation boundary condition row.leaves_allocated = new_allocation row.leaves_expired = expired_leaves - leaves_taken if expired_leaves - leaves_taken > 0 else 0 row.opening_balance = opening row.leaves_taken = leaves_taken - row.closing_balance = closing + + # not be shown on the basis of days left it create in user mind for carry_forward leave + row.closing_balance = (new_allocation + opening - (row.leaves_expired + leaves_taken)) + + row.indent = 1 data.append(row) new_leaves_allocated = 0 @@ -177,7 +180,7 @@ def get_allocated_and_expired_leaves(from_date, to_date, employee, leave_type): }, as_dict=1) for record in records: - if record.to_date <= getdate(to_date): + if record.to_date < getdate(to_date): expired_leaves += record.leaves if record.from_date >= getdate(from_date): diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index a5cdecf36a..92715d3445 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -6,7 +6,7 @@ import frappe from frappe import _ from frappe.utils import flt from erpnext.hr.doctype.leave_application.leave_application \ - import get_leave_balance_on, get_leaves_for_period + import get_leave_details from erpnext.hr.report.employee_leave_balance.employee_leave_balance \ import get_department_leave_approver_map @@ -61,14 +61,14 @@ def get_data(filters, leave_types): if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) or ("HR Manager" in frappe.get_roles(user)): row = [employee.name, employee.employee_name, employee.department] - + available_leave = get_leave_details(employee.name, filters.date) for leave_type in leave_types: - + remaining = 0 + if leave_type in available_leave["leave_allocation"]: # opening balance - opening = get_leave_balance_on(employee.name, leave_type, filters.date) + remaining = available_leave["leave_allocation"][leave_type]['remaining_leaves'] - - row += [opening] + row += [remaining] data.append(row) diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 60767b5db0..47daab1901 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -135,7 +135,7 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho row += [emp, emp_det.employee_name] total_p = total_a = total_l = total_h = total_um= 0.0 - ggg = [] + emp_status_map = [] for day in range(filters["total_days_in_month"]): status = None status = att_map.get(emp).get(day + 1) @@ -152,11 +152,10 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho status = "Holiday" total_h += 1 - ggg.append(status_map.get(status, "")) + abbr = status_map.get(status, "") + emp_status_map.append(abbr) - if not filters.summarized_view: - row += ggg - else: + if filters.summarized_view: if status == "Present" or status == "Work From Home": total_p += 1 elif status == "Absent": @@ -170,6 +169,9 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho elif not status: total_um += 1 + if not filters.summarized_view: + row += emp_status_map + if filters.summarized_view: row += [total_p, total_l, total_a, total_h, total_um] @@ -203,7 +205,7 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho row.append("0.0") row.extend([time_default_counts[0][0],time_default_counts[0][1]]) - emp_att_map[emp] = ggg + emp_att_map[emp] = emp_status_map record.append(row) return record, emp_att_map @@ -216,7 +218,7 @@ def get_columns(filters): columns = [_(filters.group_by)+ ":Link/Branch:120"] columns += [ - _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Link/Employee:120" + _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Data/:120" ] days = [] for day in range(filters["total_days_in_month"]): diff --git a/erpnext/hr/report/salary_register/salary_register.json b/erpnext/hr/report/salary_register/salary_register.json deleted file mode 100644 index 89a7ba2910..0000000000 --- a/erpnext/hr/report/salary_register/salary_register.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "add_total_row": 1, - "apply_user_permissions": 1, - "creation": "2017-01-10 17:36:58.153863", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 2, - "is_standard": "Yes", - "modified": "2017-02-24 19:58:33.143974", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Register", - "owner": "Administrator", - "ref_doctype": "Salary Slip", - "report_name": "Salary Register", - "report_type": "Script Report", - "roles": [ - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index cd125108c6..8d95924681 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -73,11 +73,11 @@ class EmployeeBoardingController(Document): def assign_task_to_users(self, task, users): for user in users: args = { - 'assign_to' : user, - 'doctype' : task.doctype, - 'name' : task.name, - 'description' : task.description or task.subject, - 'notify': self.notify_users_by_email + 'assign_to': [user], + 'doctype': task.doctype, + 'name': task.name, + 'description': task.description or task.subject, + 'notify': self.notify_users_by_email } assign_to.add(args) diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json index d79860a352..23671ea791 100644 --- a/erpnext/loan_management/desk_page/loan/loan.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -23,7 +23,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Loan Repayment\"\n ],\n \"doctype\": \"Loan Repayment\",\n \"incomplete_dependencies\": [\n \"Loan Repayment\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"doctype\": \"Loan Security Pledge\",\n \"incomplete_dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"doctype\": \"Loan Repayment\",\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"doctype\": \"Loan Security Pledge\",\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -38,7 +38,7 @@ "idx": 0, "is_standard": 1, "label": "Loan", - "modified": "2020-05-28 13:37:42.017709", + "modified": "2020-06-07 19:42:14.947902", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 364e2ffecf..3f37a26418 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -9,7 +9,7 @@ import unittest from frappe.utils import (nowdate, add_days, getdate, now_datetime, add_to_date, get_datetime, add_months, get_first_day, get_last_day, flt, date_diff) from erpnext.selling.doctype.customer.test_customer import get_customer_dict -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (process_loan_interest_accrual_for_demand_loans, process_loan_interest_accrual_for_term_loans) from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year diff --git a/erpnext/loan_management/doctype/loan_application/test_loan_application.py b/erpnext/loan_management/doctype/loan_application/test_loan_application.py index 99c807b2cd..687c58000e 100644 --- a/erpnext/loan_management/doctype/loan_application/test_loan_application.py +++ b/erpnext/loan_management/doctype/loan_application/test_loan_application.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan_accounts class TestLoanApplication(unittest.TestCase): diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index c9e36a84dd..d44088bee7 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -27,6 +27,7 @@ class LoanDisbursement(AccountsController): def on_cancel(self): self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ['GL Entry'] def set_missing_values(self): if not self.disbursement_date: diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 094b9c698c..e6ceb55185 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -31,6 +31,7 @@ class LoanInterestAccrual(AccountsController): self.update_is_accrued() self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ['GL Entry'] def update_is_accrued(self): frappe.db.set_value('Repayment Schedule', self.repayment_schedule_name, 'is_accrued', 0) @@ -176,21 +177,23 @@ def get_term_loans(date, term_loan=None, loan_type=None): return term_loans def make_loan_interest_accrual_entry(args): - loan_interest_accrual = frappe.new_doc("Loan Interest Accrual") - loan_interest_accrual.loan = args.loan - loan_interest_accrual.applicant_type = args.applicant_type - loan_interest_accrual.applicant = args.applicant - loan_interest_accrual.interest_income_account = args.interest_income_account - loan_interest_accrual.loan_account = args.loan_account - loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, 2) - loan_interest_accrual.interest_amount = flt(args.interest_amount, 2) - loan_interest_accrual.posting_date = args.posting_date or nowdate() - loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest - loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name - loan_interest_accrual.payable_principal_amount = args.payable_principal + precision = cint(frappe.db.get_default("currency_precision")) or 2 - loan_interest_accrual.save() - loan_interest_accrual.submit() + loan_interest_accrual = frappe.new_doc("Loan Interest Accrual") + loan_interest_accrual.loan = args.loan + loan_interest_accrual.applicant_type = args.applicant_type + loan_interest_accrual.applicant = args.applicant + loan_interest_accrual.interest_income_account = args.interest_income_account + loan_interest_accrual.loan_account = args.loan_account + loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, precision) + loan_interest_accrual.interest_amount = flt(args.interest_amount, precision) + loan_interest_accrual.posting_date = args.posting_date or nowdate() + loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest + loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name + loan_interest_accrual.payable_principal_amount = args.payable_principal + + loan_interest_accrual.save() + loan_interest_accrual.submit() def get_no_of_days_for_interest_accural(loan, posting_date): diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 2ab668a0e1..c28994e280 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext import json from frappe import _ -from frappe.utils import flt, getdate +from frappe.utils import flt, getdate, cint from six import iteritems from frappe.model.document import Document from frappe.utils import date_diff, add_days, getdate, add_months, get_first_day, get_datetime @@ -29,8 +29,11 @@ class LoanRepayment(AccountsController): def on_cancel(self): self.mark_as_unpaid() self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ['GL Entry'] def set_missing_values(self, amounts): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + if not self.posting_date: self.posting_date = get_datetime() @@ -38,24 +41,26 @@ class LoanRepayment(AccountsController): self.cost_center = erpnext.get_default_cost_center(self.company) if not self.interest_payable: - self.interest_payable = flt(amounts['interest_amount'], 2) + self.interest_payable = flt(amounts['interest_amount'], precision) if not self.penalty_amount: - self.penalty_amount = flt(amounts['penalty_amount'], 2) + self.penalty_amount = flt(amounts['penalty_amount'], precision) if not self.pending_principal_amount: - self.pending_principal_amount = flt(amounts['pending_principal_amount'], 2) + self.pending_principal_amount = flt(amounts['pending_principal_amount'], precision) if not self.payable_principal_amount and self.is_term_loan: - self.payable_principal_amount = flt(amounts['payable_principal_amount'], 2) + self.payable_principal_amount = flt(amounts['payable_principal_amount'], precision) if not self.payable_amount: - self.payable_amount = flt(amounts['payable_amount'], 2) + self.payable_amount = flt(amounts['payable_amount'], precision) if amounts.get('due_date'): self.due_date = amounts.get('due_date') def validate_amount(self): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + if not self.amount_paid: frappe.throw(_("Amount paid cannot be zero")) @@ -63,11 +68,13 @@ class LoanRepayment(AccountsController): msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount) frappe.throw(msg) - if self.payment_type == "Loan Closure" and flt(self.amount_paid, 2) < flt(self.payable_amount, 2): + if self.payment_type == "Loan Closure" and flt(self.amount_paid, precision) < flt(self.payable_amount, precision): msg = _("Amount of {0} is required for Loan closure").format(self.payable_amount) frappe.throw(msg) def update_paid_amount(self): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + loan = frappe.get_doc("Loan", self.against_loan) for payment in self.repayment_details: @@ -75,9 +82,9 @@ class LoanRepayment(AccountsController): SET paid_principal_amount = `paid_principal_amount` + %s, paid_interest_amount = `paid_interest_amount` + %s WHERE name = %s""", - (flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual)) + (flt(payment.paid_principal_amount, precision), flt(payment.paid_interest_amount, precision), payment.loan_interest_accrual)) - if flt(loan.total_principal_paid + self.principal_amount_paid, 2) >= flt(loan.total_payment, 2): + if flt(loan.total_principal_paid + self.principal_amount_paid, precision) >= flt(loan.total_payment, precision): if loan.is_secured_loan: frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested") else: @@ -253,6 +260,7 @@ def get_accrued_interest_entries(against_loan): # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable def get_amounts(amounts, against_loan, posting_date, payment_type): + precision = cint(frappe.db.get_default("currency_precision")) or 2 against_loan_doc = frappe.get_doc("Loan", against_loan) loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type) @@ -282,8 +290,8 @@ def get_amounts(amounts, against_loan, posting_date, payment_type): payable_principal_amount += entry.payable_principal_amount pending_accrual_entries.setdefault(entry.name, { - 'interest_amount': flt(entry.interest_amount), - 'payable_principal_amount': flt(entry.payable_principal_amount) + 'interest_amount': flt(entry.interest_amount, precision), + 'payable_principal_amount': flt(entry.payable_principal_amount, precision) }) if not final_due_date: @@ -301,11 +309,11 @@ def get_amounts(amounts, against_loan, posting_date, payment_type): per_day_interest = (payable_principal_amount * (loan_type_details.rate_of_interest / 100))/365 total_pending_interest += (pending_days * per_day_interest) - amounts["pending_principal_amount"] = pending_principal_amount - amounts["payable_principal_amount"] = payable_principal_amount - amounts["interest_amount"] = total_pending_interest - amounts["penalty_amount"] = penalty_amount - amounts["payable_amount"] = payable_principal_amount + total_pending_interest + penalty_amount + amounts["pending_principal_amount"] = flt(pending_principal_amount, precision) + amounts["payable_principal_amount"] = flt(payable_principal_amount, precision) + amounts["interest_amount"] = flt(total_pending_interest, precision) + amounts["penalty_amount"] = flt(penalty_amount, precision) + amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision) amounts["pending_accrual_entries"] = pending_accrual_entries if final_due_date: diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json index 51c5cb98a6..1dd3710cd2 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.json +++ b/erpnext/loan_management/doctype/loan_type/loan_type.json @@ -41,6 +41,7 @@ "options": "Company:company:default_currency" }, { + "default": "0", "fieldname": "rate_of_interest", "fieldtype": "Percent", "label": "Rate of Interest (%) Yearly", @@ -143,7 +144,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-15 00:24:43.259963", + "modified": "2020-06-07 18:55:59.346292", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Type", diff --git a/erpnext/loan_management/loan_common.js b/erpnext/loan_management/loan_common.js index 3a47a88cbe..d9dd415296 100644 --- a/erpnext/loan_management/loan_common.js +++ b/erpnext/loan_management/loan_common.js @@ -9,12 +9,15 @@ frappe.ui.form.on(cur_frm.doctype, { } if (['Loan Disbursement', 'Loan Repayment', 'Loan Interest Accrual'].includes(frm.doc.doctype) - && frm.doc.docstatus == 1) { + && frm.doc.docstatus > 0) { frm.add_custom_button(__("Accounting Ledger"), function() { frappe.route_options = { voucher_no: frm.doc.name, - company: frm.doc.company + company: frm.doc.company, + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), + show_cancelled_entries: frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); diff --git a/erpnext/loan_management/report/loan_security_status/loan_security_status.py b/erpnext/loan_management/report/loan_security_status/loan_security_status.py index ea6a2ee645..1951855475 100644 --- a/erpnext/loan_management/report/loan_security_status/loan_security_status.py +++ b/erpnext/loan_management/report/loan_security_status/loan_security_status.py @@ -76,7 +76,8 @@ def get_columns(filters): "fieldtype": "Link", "fieldname": "currency", "options": "Currency", - "width": 50 + "width": 50, + "hidden": 1 } ] @@ -84,17 +85,13 @@ def get_columns(filters): def get_data(filters): - loan_security_price_map = frappe._dict(frappe.get_all("Loan Security", - fields=["name", "loan_security_price"], as_list=1 - )) - data = [] conditions = get_conditions(filters) loan_security_pledges = frappe.db.sql(""" SELECT p.name, p.applicant, p.loan, p.status, p.pledge_time, - c.loan_security, c.qty + c.loan_security, c.qty, c.loan_security_price, c.amount FROM `tabLoan Security Pledge` p, `tabPledge` c WHERE @@ -115,8 +112,8 @@ def get_data(filters): row["pledge_time"] = pledge.pledge_time row["loan_security"] = pledge.loan_security row["qty"] = pledge.qty - row["loan_security_price"] = loan_security_price_map.get(pledge.loan_security) - row["loan_security_value"] = row["loan_security_price"] * pledge.qty + row["loan_security_price"] = pledge.loan_security_price + row["loan_security_value"] = pledge.amount row["currency"] = default_currency data.append(row) diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index 4a17fd07fb..0e9a21c026 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -4,7 +4,6 @@ import frappe, erpnext, json from frappe import _ from frappe.utils import nowdate, get_first_day, get_last_day, add_months -from erpnext.accounts.utils import get_fiscal_year def get_data(): return frappe._dict({ @@ -193,7 +192,7 @@ def get_number_cards(): ]), "function": "Count", "is_public": 1, - "label": _("Monthly Total Work Order"), + "label": _("Monthly Total Work Orders"), "show_percentage_stats": 1, "stats_time_interval": "Weekly" }, @@ -208,7 +207,7 @@ def get_number_cards(): ]), "function": "Count", "is_public": 1, - "label": _("Monthly Completed Work Order"), + "label": _("Monthly Completed Work Orders"), "show_percentage_stats": 1, "stats_time_interval": "Weekly" }, @@ -222,7 +221,7 @@ def get_number_cards(): ]), "function": "Count", "is_public": 1, - "label": _("Ongoing Job Card"), + "label": _("Ongoing Job Cards"), "show_percentage_stats": 1, "stats_time_interval": "Weekly" }, @@ -236,7 +235,7 @@ def get_number_cards(): ]), "function": "Count", "is_public": 1, - "label": _("Monthly Quality Inspection"), + "label": _("Monthly Quality Inspections"), "show_percentage_stats": 1, "stats_time_interval": "Weekly" }] \ No newline at end of file diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 763f533a94..8d11294164 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -93,12 +93,6 @@ "stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}", "type": "DocType" }, - { - "label": "Dashboard", - "link_to": "Manufacturing", - "restrict_to_domain": "Manufacturing", - "type": "Dashboard" - }, { "label": "Forecasting", "link_to": "Exponential Smoothing Forecasting", @@ -119,6 +113,12 @@ "label": "Production Planning Report", "link_to": "Production Planning Report", "type": "Report" - } + }, + { + "label": "Dashboard", + "link_to": "Manufacturing", + "restrict_to_domain": "Manufacturing", + "type": "Dashboard" + } ] } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 1bdac5731e..7d31a1cd15 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -112,8 +112,16 @@ class BOM(WebsiteGenerator): if self.routing: self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], - filters = {'parenttype': 'Routing', 'parent': self.routing}): - child = self.append('operations', d) + filters = {'parenttype': 'Routing', 'parent': self.routing}, order_by="idx"): + child = self.append('operations', { + "operation": d.operation, + "workstation": d.workstation, + "description": d.description, + "time_in_mins": d.time_in_mins, + "batch_size": d.batch_size, + "operating_cost": d.operating_cost, + "idx": d.idx + }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) def set_bom_material_details(self): diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json index 3ca851d783..0350e2cb37 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json @@ -78,6 +78,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_hour_rate", "fieldtype": "Currency", "label": "Base Hour Rate(Company Currency)", @@ -87,6 +88,7 @@ }, { "default": "5", + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_operating_cost", "fieldtype": "Currency", "label": "Operating Cost(Company Currency)", @@ -108,12 +110,12 @@ ], "idx": 1, "istable": 1, - "modified": "2019-07-16 22:35:55.374037", - "modified_by": "govindsmenokee@gmail.com", + "modified": "2020-06-16 17:01:11.128420", + "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Operation", "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js index 6cfd0bae5b..d7589fa390 100644 --- a/erpnext/manufacturing/doctype/routing/routing.js +++ b/erpnext/manufacturing/doctype/routing/routing.js @@ -44,7 +44,6 @@ frappe.ui.form.on('BOM Operation', { name: d.workstation }, callback: function (data) { - frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate); frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate); frm.events.calculate_operating_cost(frm, d); } diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json index 952d1f0e07..a36b63a1d9 100644 --- a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 12:51:42.744570", + "modified": "2020-06-29 20:25:36.899106", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -52,6 +52,5 @@ ], "subtitle": "Products, Raw Materials, BOM, Work Order and more.", "success_message": "Manufacturing module is all setup!", - "title": "Let's Setup Manufacturing Module", - "user_can_dismiss": 1 -} \ No newline at end of file + "title": "Let's Set Up the Manufacturing Module" +} diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py index cac8067729..2ca9f1694b 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -217,6 +217,8 @@ class ForecastingReport(ExponentialSmoothingForecast): } def get_summary_data(self): + if not self.data: return + return [ { "value": sum(self.total_demand), diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index 33953b1265..bd68db190e 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -41,7 +41,7 @@ frappe.query_reports["Job Card Summary"] = { reqd: 1 }, { - label: __("To Posting Datetime"), + label: __("To Posting Date"), fieldname:"to_date", fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js index 22928657b9..eb23f17c47 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -41,7 +41,7 @@ frappe.query_reports["Work Order Summary"] = { reqd: 1 }, { - label: __("To Posting Datetime"), + label: __("To Posting Date"), fieldname:"to_date", fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), diff --git a/erpnext/modules.txt b/erpnext/modules.txt index 3b347582c3..1e2aeea36a 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -24,4 +24,5 @@ Hotels Hub Node Quality Management Communication -Loan Management \ No newline at end of file +Loan Management +Payroll \ No newline at end of file diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py index 571f87af87..d1294ccc08 100644 --- a/erpnext/non_profit/doctype/member/member.py +++ b/erpnext/non_profit/doctype/member/member.py @@ -77,6 +77,7 @@ def create_customer(user_details): customer = frappe.new_doc("Customer") customer.customer_name = user_details.fullname customer.customer_type = "Individual" + customer.flags.ignore_mandatory = True customer.insert(ignore_permissions=True) try: @@ -91,7 +92,11 @@ def create_customer(user_details): "link_name": customer.name }) - contact.insert() + contact.save() + + except frappe.DuplicateEntryError: + return customer.name + except Exception as e: frappe.log_error(frappe.get_traceback(), _("Contact Creation Failed")) pass diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index 5a69cdb6ab..7a0caed621 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -62,10 +62,27 @@ def get_member_based_on_subscription(subscription_id, email): 'subscription_id': subscription_id, 'email_id': email }, order_by="creation desc") - return frappe.get_doc("Member", members[0]['name']) + try: + return frappe.get_doc("Member", members[0]['name']) + except: + return None + +def verify_signature(data): + signature = frappe.request.headers.get('X-Razorpay-Signature') + + settings = frappe.get_doc("Membership Settings") + key = settings.get_webhook_secret() + + controller = frappe.get_doc("Razorpay Settings") + + controller.verify_signature(data, signature, key) + @frappe.whitelist(allow_guest=True) -def trigger_razorpay_subscription(data): +def trigger_razorpay_subscription(*args, **kwargs): + data = frappe.request.get_data(as_text=True) + verify_signature(data) + if isinstance(data, six.string_types): data = json.loads(data) data = frappe._dict(data) @@ -82,7 +99,10 @@ def trigger_razorpay_subscription(data): except Exception as e: error_log = frappe.log_error(frappe.get_traceback() + '\n' + data_json , _("Membership Webhook Failed")) notify_failure(error_log) - raise e + return False + + if not member: + return False if data.event == "subscription.activated": member.customer_id = payment.customer_id @@ -111,7 +131,6 @@ def trigger_razorpay_subscription(data): return True - def notify_failure(log): try: content = """Dear System Manager, diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.js b/erpnext/non_profit/doctype/membership_settings/membership_settings.js index c01a0b23d5..8c0e3a4fa7 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.js +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.js @@ -1,8 +1,30 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Membership Settings', { +frappe.ui.form.on("Membership Settings", { refresh: function(frm) { + if (frm.doc.webhook_secret) { + frm.add_custom_button(__("Revoke "), () => { + frm.call("revoke_key").then(() => { + frm.refresh(); + }) + }); + } + frm.trigger("add_generate_button"); + }, - } + add_generate_button: function(frm) { + let label; + + if (frm.doc.webhook_secret) { + label = __("Regenerate Webhook Secret"); + } else { + label = __("Generate Webhook Secret"); + } + frm.add_custom_button(label, () => { + frm.call("generate_webhook_key").then(() => { + frm.refresh(); + }); + }); + }, }); diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.json b/erpnext/non_profit/doctype/membership_settings/membership_settings.json index 56b8eac4b1..52b9d01088 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.json @@ -8,7 +8,8 @@ "enable_razorpay", "razorpay_settings_section", "billing_cycle", - "billing_frequency" + "billing_frequency", + "webhook_secret" ], "fields": [ { @@ -34,11 +35,17 @@ "fieldname": "billing_frequency", "fieldtype": "Int", "label": "Billing Frequency" + }, + { + "fieldname": "webhook_secret", + "fieldtype": "Password", + "label": "Webhook Secret", + "read_only": 1 } ], "issingle": 1, "links": [], - "modified": "2020-04-07 18:42:51.496807", + "modified": "2020-05-22 12:38:27.103759", "modified_by": "Administrator", "module": "Non Profit", "name": "Membership Settings", diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.py b/erpnext/non_profit/doctype/membership_settings/membership_settings.py index 2b8e37f2a6..f3b2eee6f9 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.py +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.py @@ -4,11 +4,27 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.integrations.utils import get_payment_gateway_controller from frappe.model.document import Document class MembershipSettings(Document): - pass + def generate_webhook_key(self): + key = frappe.generate_hash(length=20) + self.webhook_secret = key + self.save() + + frappe.msgprint( + _("Here is your webhook secret, this will be shown to you only once.") + "

" + key, + _("Webhook Secret") + ); + + def revoke_key(self): + self.webhook_secret = None; + self.save() + + def get_webhook_secret(self): + return self.get_password(fieldname="webhook_secret", raise_exception=False) @frappe.whitelist() def get_plans_for_membership(*args, **kwargs): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1d2bb12d8c..17fbcc2190 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -333,7 +333,7 @@ erpnext.patches.v7_0.update_mode_of_payment_type execute:frappe.reload_doctype('Employee') #2016-10-18 execute:frappe.db.sql("update `tabEmployee` set prefered_contact_email = IFNULL(prefered_contact_email,'') ") -execute:frappe.reload_doctype("Salary Slip") +execute:frappe.reload_doc("Payroll", "doctype", "salary_slip") execute:frappe.db.sql("update `tabSalary Slip` set posting_date=creation") execute:frappe.reload_doc("stock", "doctype", "stock_settings") erpnext.patches.v8_0.create_domain_docs #16-05-2017 @@ -680,6 +680,8 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive +execute:frappe.reload_doc("HR", "doctype", "Employee Advance") +erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price @@ -692,4 +694,15 @@ erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2020-05-25 erpnext.patches.v12_0.update_bom_in_so_mr execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) -erpnext.patches.v13_0.delete_old_purchase_reports \ No newline at end of file +erpnext.patches.v12_0.update_uom_conversion_factor +erpnext.patches.v13_0.delete_old_purchase_reports +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions +erpnext.patches.v13_0.update_sla_enhancements +erpnext.patches.v12_0.update_address_template_for_india +erpnext.patches.v13_0.update_deferred_settings +erpnext.patches.v12_0.set_multi_uom_in_rfq +erpnext.patches.v13_0.delete_old_sales_reports +execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") +erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020 +erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020 +erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020 diff --git a/erpnext/patches/v11_0/create_department_records_for_each_company.py b/erpnext/patches/v11_0/create_department_records_for_each_company.py index f09c5b2c5d..e9b5950a13 100644 --- a/erpnext/patches/v11_0/create_department_records_for_each_company.py +++ b/erpnext/patches/v11_0/create_department_records_for_each_company.py @@ -6,8 +6,9 @@ from frappe.utils.nestedset import rebuild_tree def execute(): frappe.local.lang = frappe.db.get_default("lang") or 'en' - for doctype in ['department', 'leave_period', 'staffing_plan', 'job_opening', 'payroll_entry']: + for doctype in ['department', 'leave_period', 'staffing_plan', 'job_opening']: frappe.reload_doc("hr", "doctype", doctype) + frappe.reload_doc("Payroll", "doctype", 'payroll_entry') companies = frappe.db.get_all("Company", fields=["name", "abbr"]) departments = frappe.db.get_all("Department") diff --git a/erpnext/patches/v11_0/create_salary_structure_assignments.py b/erpnext/patches/v11_0/create_salary_structure_assignments.py index 610fa85172..c51c38182c 100644 --- a/erpnext/patches/v11_0/create_salary_structure_assignments.py +++ b/erpnext/patches/v11_0/create_salary_structure_assignments.py @@ -5,11 +5,11 @@ from __future__ import unicode_literals import frappe from datetime import datetime from frappe.utils import getdate -from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import DuplicateAssignment +from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import DuplicateAssignment def execute(): - frappe.reload_doc('hr', 'doctype', 'salary_structure') - frappe.reload_doc("hr", "doctype", "salary_structure_assignment") + frappe.reload_doc('Payroll', 'doctype', 'salary_structure') + frappe.reload_doc("Payroll", "doctype", "salary_structure_assignment") frappe.db.sql(""" delete from `tabSalary Structure Assignment` where salary_structure in (select name from `tabSalary Structure` where is_active='No' or docstatus!=1) diff --git a/erpnext/patches/v11_0/inter_state_field_for_gst.py b/erpnext/patches/v11_0/inter_state_field_for_gst.py index 48249c9bbf..730eebc01c 100644 --- a/erpnext/patches/v11_0/inter_state_field_for_gst.py +++ b/erpnext/patches/v11_0/inter_state_field_for_gst.py @@ -6,8 +6,8 @@ def execute(): company = frappe.get_all('Company', filters = {'country': 'India'}) if not company: return - frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Declaration") - frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Proof Submission") + frappe.reload_doc("Payroll", "doctype", "Employee Tax Exemption Declaration") + frappe.reload_doc("Payroll", "doctype", "Employee Tax Exemption Proof Submission") frappe.reload_doc("hr", "doctype", "Employee Grade") frappe.reload_doc("hr", "doctype", "Leave Policy") diff --git a/erpnext/patches/v11_0/set_salary_component_properties.py b/erpnext/patches/v11_0/set_salary_component_properties.py index 83fb53d2a7..2498888273 100644 --- a/erpnext/patches/v11_0/set_salary_component_properties.py +++ b/erpnext/patches/v11_0/set_salary_component_properties.py @@ -2,8 +2,8 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doc('hr', 'doctype', 'salary_detail') - frappe.reload_doc('hr', 'doctype', 'salary_component') + frappe.reload_doc('Payroll', 'doctype', 'salary_detail') + frappe.reload_doc('Payroll', 'doctype', 'salary_component') frappe.db.sql("update `tabSalary Component` set is_tax_applicable=1 where type='Earning'") diff --git a/erpnext/patches/v11_1/rename_depends_on_lwp.py b/erpnext/patches/v11_1/rename_depends_on_lwp.py index 20d8867e1d..a0f2536f7d 100644 --- a/erpnext/patches/v11_1/rename_depends_on_lwp.py +++ b/erpnext/patches/v11_1/rename_depends_on_lwp.py @@ -9,5 +9,5 @@ from frappe.model.utils.rename_field import rename_field def execute(): for doctype in ("Salary Component", "Salary Detail"): if "depends_on_lwp" in frappe.db.get_table_columns(doctype): - frappe.reload_doc("hr", "doctype", scrub(doctype)) + frappe.reload_doc("Payroll", "doctype", scrub(doctype)) rename_field(doctype, "depends_on_lwp", "depends_on_payment_days") \ No newline at end of file diff --git a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py index 3c9758eb84..1ddbae6cd2 100644 --- a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py +++ b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py @@ -4,7 +4,7 @@ import frappe def execute(): frappe.reload_doc('accounts', 'doctype', 'bank', force=1) - if frappe.db.table_exists('Bank') and frappe.db.table_exists('Bank Account'): + if frappe.db.table_exists('Bank') and frappe.db.table_exists('Bank Account') and frappe.db.has_column('Bank Account', 'swift_number'): frappe.db.sql(""" UPDATE `tabBank` b, `tabBank Account` ba SET b.swift_number = ba.swift_number, b.branch_code = ba.branch_code @@ -12,4 +12,4 @@ def execute(): """) frappe.reload_doc('accounts', 'doctype', 'bank_account') - frappe.reload_doc('accounts', 'doctype', 'payment_request') \ No newline at end of file + frappe.reload_doc('accounts', 'doctype', 'payment_request') diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py new file mode 100644 index 0000000000..6013eaa29c --- /dev/null +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -0,0 +1,11 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + ''' Move from due_advance_amount to pending_amount ''' + + if frappe.db.has_column("Employee Advance", "due_advance_amount"): + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') diff --git a/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py new file mode 100644 index 0000000000..a6011c4dac --- /dev/null +++ b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py @@ -0,0 +1,12 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.italy.setup import add_permissions + +def execute(): + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + add_permissions() \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py new file mode 100644 index 0000000000..70ca6b222e --- /dev/null +++ b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py @@ -0,0 +1,16 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import flt +from erpnext.stock.get_item_details import get_conversion_factor + +def execute(): + frappe.reload_doc('buying', 'doctype', 'request_for_quotation_item') + + frappe.db.sql("""UPDATE `tabRequest for Quotation Item` + SET + stock_uom = uom, + conversion_factor = 1, + stock_qty = qty""") \ No newline at end of file diff --git a/erpnext/patches/v12_0/update_address_template_for_india.py b/erpnext/patches/v12_0/update_address_template_for_india.py new file mode 100644 index 0000000000..0d582da4b5 --- /dev/null +++ b/erpnext/patches/v12_0/update_address_template_for_india.py @@ -0,0 +1,12 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.address_template.setup import set_up_address_templates + +def execute(): + if frappe.db.get_value('Company', {'country': 'India'}, 'name'): + address_template = frappe.db.get_value('Address Template', 'India', 'template') + if not address_template or "gstin" not in address_template: + set_up_address_templates(default_country='India') diff --git a/erpnext/patches/v12_0/update_uom_conversion_factor.py b/erpnext/patches/v12_0/update_uom_conversion_factor.py new file mode 100644 index 0000000000..b5a20aa6fd --- /dev/null +++ b/erpnext/patches/v12_0/update_uom_conversion_factor.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals +import frappe, json + +def execute(): + from erpnext.setup.setup_wizard.operations.install_fixtures import add_uom_data + + frappe.reload_doc("setup", "doctype", "UOM Conversion Factor") + frappe.reload_doc("setup", "doctype", "UOM") + frappe.reload_doc("stock", "doctype", "UOM Category") + + add_uom_data() \ No newline at end of file diff --git a/erpnext/patches/v13_0/check_is_income_tax_component.py b/erpnext/patches/v13_0/check_is_income_tax_component.py new file mode 100644 index 0000000000..9ad48e23b7 --- /dev/null +++ b/erpnext/patches/v13_0/check_is_income_tax_component.py @@ -0,0 +1,43 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe, erpnext +from erpnext.regional.india.setup import setup + +def execute(): + + doctypes = ['salary_component', + 'Employee Tax Exemption Declaration', + 'Employee Tax Exemption Proof Submission', + 'Employee Tax Exemption Declaration Category', + 'Employee Tax Exemption Proof Submission Detail' + ] + + for doctype in doctypes: + frappe.reload_doc('Payroll', 'doctype', doctype) + + + reports = ['Professional Tax Deductions', 'Provident Fund Deductions'] + for report in reports: + frappe.reload_doc('Regional', 'Report', report) + frappe.reload_doc('Regional', 'Report', report) + + if erpnext.get_region() == "India": + setup(patch=True) + + if frappe.db.exists("Salary Component", "Income Tax"): + frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1) + if frappe.db.exists("Salary Component", "TDS"): + frappe.db.set_value("Salary Component", "TDS", "is_income_tax_component", 1) + + components = frappe.db.sql("select name from `tabSalary Component` where variable_based_on_taxable_salary = 1", as_dict=1) + for component in components: + frappe.db.set_value("Salary Component", component.name, "is_income_tax_component", 1) + + if erpnext.get_region() == "India": + if frappe.db.exists("Salary Component", "Provident Fund"): + frappe.db.set_value("Salary Component", "Provident Fund", "component_type", "Provident Fund") + if frappe.db.exists("Salary Component", "Professional Tax"): + frappe.db.set_value("Salary Component", "Professional Tax", "component_type", "Professional Tax") \ No newline at end of file diff --git a/erpnext/patches/v13_0/delete_old_sales_reports.py b/erpnext/patches/v13_0/delete_old_sales_reports.py new file mode 100644 index 0000000000..0f44865808 --- /dev/null +++ b/erpnext/patches/v13_0/delete_old_sales_reports.py @@ -0,0 +1,21 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + reports_to_delete = ["Ordered Items To Be Delivered", "Ordered Items To Be Billed"] + + for report in reports_to_delete: + if frappe.db.exists("Report", report): + delete_auto_email_reports(report) + + frappe.delete_doc("Report", report) + +def delete_auto_email_reports(report): + """ Check for one or multiple Auto Email Reports and delete """ + auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"]) + for auto_email_report in auto_email_reports: + frappe.delete_doc("Auto Email Report", auto_email_report[0]) \ No newline at end of file diff --git a/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py b/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py new file mode 100644 index 0000000000..4d7c85ce2d --- /dev/null +++ b/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py @@ -0,0 +1,52 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + frappe.db.sql("""UPDATE `tabPrint Format` + SET module = 'Payroll' + WHERE name IN ('Salary Slip Based On Timesheet', 'Salary Slip Standard')""" + ) + + frappe.db.sql("""UPDATE `tabNotification` SET module='Payroll' WHERE name='Retention Bonus';""" + ) + + doctypes_moved = [ + 'Employee Benefit Application Detail', + 'Employee Tax Exemption Declaration Category', + 'Salary Component', + 'Employee Tax Exemption Proof Submission Detail', + 'Income Tax Slab Other Charges', + 'Taxable Salary Slab', + 'Payroll Period Date', + 'Salary Slip Timesheet', + 'Payroll Employee Detail', + 'Salary Detail', + 'Employee Tax Exemption Sub Category', + 'Employee Tax Exemption Category', + 'Employee Benefit Claim', + 'Employee Benefit Application', + 'Employee Other Income', + 'Employee Tax Exemption Proof Submission', + 'Employee Tax Exemption Declaration', + 'Employee Incentive', + 'Retention Bonus', + 'Additional Salary', + 'Income Tax Slab', + 'Payroll Period', + 'Salary Slip', + 'Payroll Entry', + 'Salary Structure Assignment', + 'Salary Structure' + ] + + for doctype in doctypes_moved: + frappe.delete_doc_if_exists("DocType", doctype) + + reports = ["Salary Register", "Bank Remittance"] + + for report in reports: + frappe.delete_doc_if_exists("Report", report) diff --git a/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py b/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py new file mode 100644 index 0000000000..a901064b88 --- /dev/null +++ b/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py @@ -0,0 +1,27 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + data = frappe.db.sql('''SELECT * + FROM `tabSingles` + WHERE + doctype = "HR Settings" + AND + field in ( + "encrypt_salary_slips_in_emails", + "email_salary_slip_to_employee", + "daily_wages_fraction_for_half_day", + "disable_rounded_total", + "include_holidays_in_total_working_days", + "max_working_hours_against_timesheet", + "payroll_based_on", + "password_policy" + ) + ''', as_dict=1) + + for d in data: + frappe.db.set_value("Payroll Settings", None, d.field, d.value) diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py index ec94cd01d1..1a91d218ba 100644 --- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py +++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -11,19 +11,25 @@ def execute(): return for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"): - frappe.reload_doc("hr", "doctype", doctype) + frappe.reload_doc("Payroll", "doctype", doctype) + standard_tax_exemption_amount_exists = frappe.db.has_column("Payroll Period", "standard_tax_exemption_amount") + + select_fields = "name, start_date, end_date" + if standard_tax_exemption_amount_exists: + select_fields = "name, start_date, end_date, standard_tax_exemption_amount" + for company in frappe.get_all("Company"): payroll_periods = frappe.db.sql(""" SELECT - name, start_date, end_date, standard_tax_exemption_amount + {0} FROM `tabPayroll Period` WHERE company=%s ORDER BY start_date DESC - """, company.name, as_dict = 1) - + """.format(select_fields), company.name, as_dict = 1) + for i, period in enumerate(payroll_periods): income_tax_slab = frappe.new_doc("Income Tax Slab") income_tax_slab.name = "Tax Slab:" + period.name @@ -36,7 +42,8 @@ def execute(): income_tax_slab.effective_from = period.start_date income_tax_slab.company = company.name income_tax_slab.allow_tax_exemption = 1 - income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount + if standard_tax_exemption_amount_exists: + income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount income_tax_slab.flags.ignore_mandatory = True income_tax_slab.submit() diff --git a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py index ddcadcb4de..fde8f86470 100644 --- a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py +++ b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py @@ -6,8 +6,10 @@ def execute(): if not frappe.db.table_exists("Additional Salary"): return - for doctype in ("Additional Salary", "Leave Encashment", "Employee Incentive", "Salary Detail"): - frappe.reload_doc("hr", "doctype", doctype) + for doctype in ("Additional Salary", "Employee Incentive", "Salary Detail"): + frappe.reload_doc("Payroll", "doctype", doctype) + + frappe.reload_doc("hr", "doctype", "Leave Encashment") additional_salaries = frappe.get_all("Additional Salary", fields = ['name', "salary_slip", "type", "salary_component"], diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py index a7d4c665a1..be5e30f307 100644 --- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -7,4 +7,4 @@ def execute(): for entry in doctypes: if frappe.db.exists('DocType', entry): frappe.reload_doc('Healthcare', 'doctype', entry) - frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) + frappe.db.sql("update `tab{dt}` set company = {company} where ifnull(company, '') = ''".format(dt=entry, company=frappe.db.escape(company))) diff --git a/erpnext/patches/v13_0/update_deferred_settings.py b/erpnext/patches/v13_0/update_deferred_settings.py new file mode 100644 index 0000000000..a7d82077b7 --- /dev/null +++ b/erpnext/patches/v13_0/update_deferred_settings.py @@ -0,0 +1,11 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +import frappe + +def execute(): + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.book_deferred_entries_based_on = 'Days' + accounts_settings.book_deferred_entries_via_journal_entry = 0 + accounts_settings.submit_journal_entries = 0 + accounts_settings.save() \ No newline at end of file diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py new file mode 100644 index 0000000000..c156ba9577 --- /dev/null +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -0,0 +1,93 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + # add holiday list and employee group fields in SLA + # change response and resolution time in priorities child table + if frappe.db.exists('DocType', 'Service Level Agreement'): + sla_details = frappe.db.get_all('Service Level Agreement', fields=['name', 'service_level']) + priorities = frappe.db.get_all('Service Level Priority', fields=['*'], filters={ + 'parenttype': ('in', ['Service Level Agreement', 'Service Level']) + }) + + frappe.reload_doc('support', 'doctype', 'service_level_agreement') + frappe.reload_doc('support', 'doctype', 'pause_sla_on_status') + frappe.reload_doc('support', 'doctype', 'service_level_priority') + frappe.reload_doc('support', 'doctype', 'service_day') + + for entry in sla_details: + values = frappe.db.get_value('Service Level', entry.service_level, ['holiday_list', 'employee_group']) + if values: + holiday_list = values[0] + employee_group = values[1] + frappe.db.set_value('Service Level Agreement', entry.name, { + 'holiday_list': holiday_list, + 'employee_group': employee_group + }) + + priority_dict = {} + + for priority in priorities: + if priority.parenttype == 'Service Level Agreement': + response_time = convert_to_seconds(priority.response_time, priority.response_time_period) + resolution_time = convert_to_seconds(priority.resolution_time, priority.resolution_time_period) + frappe.db.set_value('Service Level Priority', priority.name, { + 'response_time': response_time, + 'resolution_time': resolution_time + }) + if priority.parenttype == 'Service Level': + if not priority.parent in priority_dict: + priority_dict[priority.parent] = [] + priority_dict[priority.parent].append(priority) + + + # copy Service Levels to Service Level Agreements + sl = [entry.service_level for entry in sla_details] + if frappe.db.exists('DocType', 'Service Level'): + service_levels = frappe.db.get_all('Service Level', filters={'service_level': ('not in', sl)}, fields=['*']) + for entry in service_levels: + sla = frappe.new_doc('Service Level Agreement') + sla.service_level = entry.service_level + sla.holiday_list = entry.holiday_list + sla.employee_group = entry.employee_group + sla.flags.ignore_validate = True + sla = sla.insert(ignore_mandatory=True) + + frappe.db.sql(""" + UPDATE + `tabService Day` + SET + parent = %(new_parent)s , parentfield = 'support_and_resolution', parenttype = 'Service Level Agreement' + WHERE + parent = %(old_parent)s + """, {'new_parent': sla.name, 'old_parent': entry.name}, as_dict = 1) + + priority_list = priority_dict.get(entry.name) + if priority_list: + sla = frappe.get_doc('Service Level Agreement', sla.name) + for priority in priority_list: + row = sla.append('priorities', { + 'priority': priority.priority, + 'default_priority': priority.default_priority, + 'response_time': convert_to_seconds(priority.response_time, priority.response_time_period), + 'resolution_time': convert_to_seconds(priority.resolution_time, priority.resolution_time_period) + }) + row.db_update() + sla.db_update() + + frappe.delete_doc_if_exists('DocType', 'Service Level') + + +def convert_to_seconds(value, unit): + seconds = 0 + if unit == "Hour": + seconds = value * 3600 + if unit == "Day": + seconds = value * 3600 * 24 + if unit == "Week": + seconds = value * 3600 * 24 * 7 + return seconds diff --git a/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py b/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py index 9c602a3f78..fe50e444b5 100644 --- a/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py +++ b/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py @@ -8,7 +8,7 @@ from frappe.custom.doctype.custom_field.custom_field import create_custom_field_ def execute(): frappe.reload_doc("stock", "doctype", "purchase_receipt") frappe.reload_doc("hr", "doctype", "employee") - frappe.reload_doc("hr", "doctype", "salary_slip") + frappe.reload_doc("Payroll", "doctype", "salary_slip") india_specific_fields = { "Purchase Receipt": [{ diff --git a/erpnext/patches/v5_0/rename_table_fieldnames.py b/erpnext/patches/v5_0/rename_table_fieldnames.py index 59f534303f..aefb0a2037 100644 --- a/erpnext/patches/v5_0/rename_table_fieldnames.py +++ b/erpnext/patches/v5_0/rename_table_fieldnames.py @@ -220,7 +220,7 @@ def execute(): frappe.reload_doc("manufacturing", "doctype", "work_order_operation") frappe.reload_doc("manufacturing", "doctype", "workstation_working_hour") frappe.reload_doc("stock", "doctype", "item_variant") - frappe.reload_doc("hr", "doctype", "salary_detail") + frappe.reload_doc("Payroll", "doctype", "salary_detail") frappe.reload_doc("accounts", "doctype", "party_account") frappe.reload_doc("accounts", "doctype", "fiscal_year_company") diff --git a/erpnext/patches/v7_0/rename_salary_components.py b/erpnext/patches/v7_0/rename_salary_components.py index bc48e34317..1693f3bdf1 100644 --- a/erpnext/patches/v7_0/rename_salary_components.py +++ b/erpnext/patches/v7_0/rename_salary_components.py @@ -6,8 +6,8 @@ def execute(): if not frappe.db.exists("DocType", "Salary Structure Earning"): return - frappe.reload_doc("hr", "doctype", "salary_detail") - frappe.reload_doc("hr", "doctype", "salary_component") + frappe.reload_doc("Payroll", "doctype", "salary_detail") + frappe.reload_doc("Payroll", "doctype", "salary_component") standard_cols = ["name", "creation", "modified", "owner", "modified_by", "parent", "parenttype", "parentfield", "idx"] diff --git a/erpnext/patches/v7_0/update_mins_to_first_response.py b/erpnext/patches/v7_0/update_mins_to_first_response.py index 1df4b42ced..16681357e6 100644 --- a/erpnext/patches/v7_0/update_mins_to_first_response.py +++ b/erpnext/patches/v7_0/update_mins_to_first_response.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe -from frappe.core.doctype.communication.email import update_mins_to_first_communication +from frappe.core.doctype.communication.communication import update_mins_to_first_communication def execute(): frappe.reload_doctype('Issue') diff --git a/erpnext/patches/v7_1/update_component_type.py b/erpnext/patches/v7_1/update_component_type.py index 552fc89467..24ca0570e0 100644 --- a/erpnext/patches/v7_1/update_component_type.py +++ b/erpnext/patches/v7_1/update_component_type.py @@ -3,7 +3,7 @@ import frappe from frappe.utils import flt def execute(): - frappe.reload_doc('hr', 'doctype', 'salary_component') + frappe.reload_doc('Payroll', 'doctype', 'salary_component') sal_components = frappe.db.sql(""" select DISTINCT salary_component, parentfield from `tabSalary Detail`""", as_dict=True) diff --git a/erpnext/patches/v7_1/update_missing_salary_component_type.py b/erpnext/patches/v7_1/update_missing_salary_component_type.py index 7d50ee4dff..824f2b881f 100644 --- a/erpnext/patches/v7_1/update_missing_salary_component_type.py +++ b/erpnext/patches/v7_1/update_missing_salary_component_type.py @@ -10,12 +10,12 @@ earnings or deductions in existing salary slips def execute(): frappe.reload_doc("accounts", "doctype", "salary_component_account") - frappe.reload_doc("hr", "doctype", "salary_component") - frappe.reload_doc("hr", "doctype", "taxable_salary_slab") - - for s in frappe.db.sql('''select name, type, salary_component_abbr from `tabSalary Component` + frappe.reload_doc("Payroll", "doctype", "salary_component") + frappe.reload_doc("Payroll", "doctype", "taxable_salary_slab") + + for s in frappe.db.sql('''select name, type, salary_component_abbr from `tabSalary Component` where ifnull(type, "")="" or ifnull(salary_component_abbr, "") = ""''', as_dict=1): - + component = frappe.get_doc('Salary Component', s.name) # guess @@ -29,22 +29,22 @@ def execute(): else: component.type = 'Deduction' - + if not s.salary_component_abbr: abbr = ''.join([c[0] for c in component.salary_component.split()]).upper() - + abbr_count = frappe.db.sql(""" - select - count(name) - from - `tabSalary Component` - where + select + count(name) + from + `tabSalary Component` + where salary_component_abbr = %s or salary_component_abbr like %s """, (abbr, abbr + "-%%")) - + if abbr_count and abbr_count[0][0] > 0: abbr = abbr + "-" + cstr(abbr_count[0][0]) - + component.salary_component_abbr = abbr - + component.save() diff --git a/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py b/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py index 3b9642dd3b..d2583b9422 100644 --- a/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py +++ b/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py @@ -2,7 +2,9 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doctype('Salary Slip', 'Salary Component') + # frappe.reload_doctype('Salary Slip', 'Salary Component') + frappe.reload_doc("Payroll", "doctype", "Salary Slip") + frappe.reload_doc("Payroll", "doctype", "Salary Component") salary_components = [['Arrear', "ARR"], ['Leave Encashment', 'LENC']] for salary_component, salary_abbr in salary_components: if not frappe.db.exists('Salary Component', salary_component): diff --git a/erpnext/patches/v7_2/update_abbr_in_salary_slips.py b/erpnext/patches/v7_2/update_abbr_in_salary_slips.py index 19dcb5e3b2..57432fe986 100644 --- a/erpnext/patches/v7_2/update_abbr_in_salary_slips.py +++ b/erpnext/patches/v7_2/update_abbr_in_salary_slips.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doctype('Salary Slip') + frappe.reload_doc('Payroll', 'doctype', 'Salary Slip') if not frappe.db.has_column('Salary Detail', 'abbr'): return diff --git a/erpnext/patches/v7_2/update_salary_slips.py b/erpnext/patches/v7_2/update_salary_slips.py index 11a52f9587..9fcce62d8f 100644 --- a/erpnext/patches/v7_2/update_salary_slips.py +++ b/erpnext/patches/v7_2/update_salary_slips.py @@ -1,10 +1,10 @@ from __future__ import unicode_literals import frappe -from erpnext.hr.doctype.payroll_entry.payroll_entry import get_month_details +from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details from frappe.utils import cint def execute(): - frappe.reload_doctype('Salary Slip') + frappe.reload_doc("Payroll", "doctype", "Salary Slip") if not frappe.db.has_column('Salary Slip', 'fiscal_year'): return diff --git a/erpnext/patches/v8_7/sync_india_custom_fields.py b/erpnext/patches/v8_7/sync_india_custom_fields.py index 73e1b182e8..eb24a90f01 100644 --- a/erpnext/patches/v8_7/sync_india_custom_fields.py +++ b/erpnext/patches/v8_7/sync_india_custom_fields.py @@ -7,11 +7,11 @@ def execute(): if not company: return - frappe.reload_doc('hr', 'doctype', 'payroll_period') - frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration') - frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission') - frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration_category') - frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission_detail') + frappe.reload_doc('Payroll', 'doctype', 'payroll_period') + frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_declaration') + frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_proof_submission') + frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_declaration_category') + frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_proof_submission_detail') frappe.reload_doc('accounts', 'doctype', 'tax_category') diff --git a/erpnext/patches/v9_0/update_employee_loan_details.py b/erpnext/patches/v9_0/update_employee_loan_details.py index 86690fc1f8..ef8d32855f 100644 --- a/erpnext/patches/v9_0/update_employee_loan_details.py +++ b/erpnext/patches/v9_0/update_employee_loan_details.py @@ -5,8 +5,8 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doc('hr', 'doctype', 'salary_slip_loan') - frappe.reload_doc('hr', 'doctype', 'salary_slip') + frappe.reload_doc('Payroll', 'doctype', 'salary_slip_loan') + frappe.reload_doc('Payroll', 'doctype', 'salary_slip') for data in frappe.db.sql(""" select name, start_date, end_date, total_loan_repayment diff --git a/erpnext/hr/doctype/additional_salary/__init__.py b/erpnext/payroll/__init__.py similarity index 100% rename from erpnext/hr/doctype/additional_salary/__init__.py rename to erpnext/payroll/__init__.py diff --git a/erpnext/payroll/dashboard_fixtures.py b/erpnext/payroll/dashboard_fixtures.py new file mode 100644 index 0000000000..ae7a9ff51a --- /dev/null +++ b/erpnext/payroll/dashboard_fixtures.py @@ -0,0 +1,100 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import erpnext +from erpnext.hr.dashboard_fixtures import get_dashboards_chart_doc, get_number_cards_doc +import json +from frappe import _ + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + dashboards = [] + dashboards.append(get_payroll_dashboard()) + return dashboards + +def get_payroll_dashboard(): + return { + "name": "Payroll", + "dashboard_name": "Payroll", + "is_default": 1, + "charts": [ + { "chart": "Outgoing Salary", "width": "Full"}, + { "chart": "Designation Wise Salary(Last Month)", "width": "Half"}, + { "chart": "Department Wise Salary(Last Month)", "width": "Half"}, + ], + "cards": [ + {"card": "Total Declaration Submitted"}, + {"card": "Total Salary Structure"}, + {"card": "Total Incentive Given(Last month)"}, + {"card": "Total Outgoing Salary(Last month)"}, + ] + } + +def get_charts(): + dashboard_charts= [ + get_dashboards_chart_doc('Outgoing Salary', "Sum", "Line", + document_type = "Salary Slip", based_on="end_date", + value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1, + filters_json = json.dumps([["Salary Slip", "docstatus", "=", 1]])) + ] + + dashboard_charts.append( + get_dashboards_chart_doc('Department Wise Salary(Last Month)', "Group By", "Bar", + document_type = "Salary Slip", group_by_type="Sum", group_by_based_on="department", + time_interval = "Monthly", aggregate_function_based_on = "rounded_total", + filters_json = json.dumps([ + ["Salary Slip", "docstatus", "=", 1], + ["Salary Slip", "start_date", "Previous","1 month"] + ]) + ) + ) + + dashboard_charts.append( + get_dashboards_chart_doc('Designation Wise Salary(Last Month)', "Group By", "Bar", + document_type = "Salary Slip", group_by_type="Sum", group_by_based_on="designation", + time_interval = "Monthly", aggregate_function_based_on = "rounded_total", + filters_json = json.dumps([ + ["Salary Slip", "docstatus", "=", 1], + ["Salary Slip", "start_date", "Previous","1 month"] + ]) + ) + ) + + return dashboard_charts + +def get_number_cards(): + number_cards = [get_number_cards_doc("Employee Tax Exemption Declaration", "Total Declaration Submitted", filters_json = json.dumps([ + ["Employee Tax Exemption Declaration", "docstatus", "=","1"], + ["Employee Tax Exemption Declaration","creation","Previous","1 year"] + ]) + )] + + number_cards.append(get_number_cards_doc("Employee Incentive", "Total Incentive Given(Last month)", + time_interval = "Monthly", func = "Sum", aggregate_function_based_on = "incentive_amount", + filters_json = json.dumps([ + ["Employee Incentive", "docstatus", "=", 1], + ["Employee Incentive","payroll_date","Previous","1 year"] + ])) + ) + + number_cards.append(get_number_cards_doc("Salary Slip", "Total Outgoing Salary(Last month)", + time_interval = "Monthly", time_span= "Monthly", func = "Sum", aggregate_function_based_on = "rounded_total", + filters_json = json.dumps([ + ["Salary Slip", "docstatus", "=", 1], + ["Salary Slip", "start_date","Previous","1 month"] + ])) + ) + number_cards.append(get_number_cards_doc("Salary Structure", "Total Salary Structure", + filters_json = json.dumps([ + ["Salary Structure", "docstatus", "=", 1] + ])) + ) + + return number_cards \ No newline at end of file diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json new file mode 100644 index 0000000000..b5eac465c8 --- /dev/null +++ b/erpnext/payroll/desk_page/payroll/payroll.json @@ -0,0 +1,84 @@ +{ + "cards": [ + { + "hidden": 0, + "label": "Payroll", + "links": "[\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Taxation", + "links": "[\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n \n }\n]" + }, + { + "hidden": 0, + "label": "Compensations", + "links": "[\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n \n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Salary Payments Based On Payment Mode\",\n \"is_query_report\": true,\n \"name\": \"Salary Payments Based On Payment Mode\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Salary Payments via ECS\",\n \"is_query_report\": true,\n \"name\": \"Salary Payments via ECS\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Income Tax Deductions\",\n \"is_query_report\": true,\n \"name\": \"Income Tax Deductions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Professional Tax Deductions\",\n \"is_query_report\": true,\n \"name\": \"Professional Tax Deductions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Provident Fund Deductions\",\n \"is_query_report\": true,\n \"name\": \"Provident Fund Deductions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Payroll Entry\"\n ],\n \"doctype\": \"Payroll Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Remittance\",\n \"name\": \"Bank Remittance\",\n \"type\": \"report\"\n \n }\n]" + } + ], + "category": "Modules", + "charts": [ + { + "chart_name": "Outgoing Salary", + "label": "Outgoing Salary" + } + ], + "creation": "2020-05-27 19:54:23.405607", + "developer_mode_only": 0, + "disable_user_customization": 0, + "docstatus": 0, + "doctype": "Desk Page", + "extends_another_page": 0, + "hide_custom": 0, + "idx": 0, + "is_standard": 1, + "label": "Payroll", + "modified": "2020-06-19 12:23:06.034046", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll", + "onboarding": "Payroll", + "owner": "Administrator", + "pin_to_bottom": 0, + "pin_to_top": 0, + "shortcuts": [ + { + "label": "Salary Structure", + "link_to": "Salary Structure", + "type": "DocType" + }, + { + "label": "Payroll Entry", + "link_to": "Payroll Entry", + "type": "DocType" + }, + { + "color": "", + "format": "{} Pending", + "label": "Salary Slip", + "link_to": "Salary Slip", + "stats_filter": "{\"status\": \"Draft\"}", + "type": "DocType" + }, + { + "label": "Income Tax Slab", + "link_to": "Income Tax Slab", + "type": "DocType" + }, + { + "label": "Salary Register", + "link_to": "Salary Register", + "type": "Report" + }, + { + "label": "Dashboard", + "link_to": "Payroll", + "type": "Dashboard" + } + ] +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_application/__init__.py b/erpnext/payroll/doctype/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_application/__init__.py rename to erpnext/payroll/doctype/__init__.py diff --git a/erpnext/hr/doctype/employee_benefit_application_detail/__init__.py b/erpnext/payroll/doctype/additional_salary/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_application_detail/__init__.py rename to erpnext/payroll/doctype/additional_salary/__init__.py diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js similarity index 100% rename from erpnext/hr/doctype/additional_salary/additional_salary.js rename to erpnext/payroll/doctype/additional_salary/additional_salary.js diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.json b/erpnext/payroll/doctype/additional_salary/additional_salary.json similarity index 98% rename from erpnext/hr/doctype/additional_salary/additional_salary.json rename to erpnext/payroll/doctype/additional_salary/additional_salary.json index bfb543f49a..69cb5da893 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.json +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.json @@ -146,7 +146,7 @@ "label": "To Date", "mandatory_depends_on": "eval:(doc.is_recurring==1)" }, - { + { "fieldname": "ref_doctype", "fieldtype": "Link", "label": "Reference Document Type", @@ -163,9 +163,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-04 18:06:29.170878", + "modified": "2020-06-22 21:10:50.374063", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Additional Salary", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py similarity index 100% rename from erpnext/hr/doctype/additional_salary/additional_salary.py rename to erpnext/payroll/doctype/additional_salary/additional_salary.py diff --git a/erpnext/hr/doctype/additional_salary/test_additional_salary.js b/erpnext/payroll/doctype/additional_salary/test_additional_salary.js similarity index 100% rename from erpnext/hr/doctype/additional_salary/test_additional_salary.js rename to erpnext/payroll/doctype/additional_salary/test_additional_salary.js diff --git a/erpnext/hr/doctype/additional_salary/test_additional_salary.py b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py similarity index 87% rename from erpnext/hr/doctype/additional_salary/test_additional_salary.py rename to erpnext/payroll/doctype/additional_salary/test_additional_salary.py index 6f93fb5df8..de26543b57 100644 --- a/erpnext/hr/doctype/additional_salary/test_additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py @@ -6,8 +6,8 @@ import unittest import frappe, erpnext from frappe.utils import nowdate, add_days from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.salary_component.test_salary_component import create_salary_component -from erpnext.hr.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test +from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component +from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test class TestAdditionalSalary(unittest.TestCase): diff --git a/erpnext/hr/doctype/employee_benefit_claim/__init__.py b/erpnext/payroll/doctype/employee_benefit_application/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_claim/__init__.py rename to erpnext/payroll/doctype/employee_benefit_application/__init__.py diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js similarity index 83% rename from erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js rename to erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js index b73dcf8ac3..f509df31e8 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js @@ -6,7 +6,7 @@ frappe.ui.form.on('Employee Benefit Application', { frm.trigger('set_earning_component'); var method, args; if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ - method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; + method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; args = { employee: frm.doc.employee, on_date: frm.doc.date, @@ -15,7 +15,7 @@ frappe.ui.form.on('Employee Benefit Application', { get_max_benefits(frm, method, args); } else if(frm.doc.employee && frm.doc.date){ - method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits"; + method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits"; args = { employee: frm.doc.employee, on_date: frm.doc.date @@ -32,7 +32,7 @@ frappe.ui.form.on('Employee Benefit Application', { if(!frm.doc.employee && !frm.doc.date) return; frm.set_query("earning_component", "employee_benefits", function() { return { - query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", + query : "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", filters: {date: frm.doc.date, employee: frm.doc.employee} }; }); @@ -41,7 +41,7 @@ frappe.ui.form.on('Employee Benefit Application', { payroll_period: function(frm) { var method, args; if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ - method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; + method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; args = { employee: frm.doc.employee, on_date: frm.doc.date, diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json new file mode 100644 index 0000000000..b0c1bd6c3e --- /dev/null +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json @@ -0,0 +1,191 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-BEN-APP-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:31:39.190787", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "max_benefits", + "remaining_benefit", + "column_break_2", + "date", + "payroll_period", + "department", + "amended_from", + "section_break_4", + "employee_benefits", + "totals", + "total_amount", + "pro_rata_dispensed_amount" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "max_benefits", + "fieldtype": "Currency", + "label": "Max Benefits (Yearly)", + "read_only": 1 + }, + { + "fieldname": "remaining_benefit", + "fieldtype": "Currency", + "label": "Remaining Benefits (Yearly)", + "read_only": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "label": "Date", + "reqd": 1 + }, + { + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Benefit Application", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Benefits Applied" + }, + { + "fieldname": "employee_benefits", + "fieldtype": "Table", + "label": "Employee Benefits", + "options": "Employee Benefit Application Detail", + "reqd": 1 + }, + { + "fieldname": "totals", + "fieldtype": "Section Break", + "label": "Totals" + }, + { + "fieldname": "total_amount", + "fieldtype": "Currency", + "label": "Total Amount", + "read_only": 1 + }, + { + "fieldname": "pro_rata_dispensed_amount", + "fieldtype": "Currency", + "label": "Dispensed Amount (Pro-rated)", + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-06-22 22:58:31.271922", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Benefit Application", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py similarity index 97% rename from erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py rename to erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py index feaa92590a..e166a704d6 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -7,8 +7,8 @@ import frappe from frappe import _ from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt from frappe.model.document import Document -from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor -from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure +from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor +from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount class EmployeeBenefitApplication(Document): diff --git a/erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js similarity index 100% rename from erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.js rename to erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js diff --git a/erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.py rename to erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py diff --git a/erpnext/hr/doctype/employee_incentive/__init__.py b/erpnext/payroll/doctype/employee_benefit_application_detail/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_incentive/__init__.py rename to erpnext/payroll/doctype/employee_benefit_application_detail/__init__.py diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json new file mode 100644 index 0000000000..fa6b4da2af --- /dev/null +++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json @@ -0,0 +1,58 @@ +{ + "actions": [], + "creation": "2018-04-13 16:36:18.389786", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "earning_component", + "pay_against_benefit_claim", + "max_benefit_amount", + "amount" + ], + "fields": [ + { + "fieldname": "earning_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Earning Component", + "options": "Salary Component", + "reqd": 1 + }, + { + "default": "0", + "fetch_from": "earning_component.pay_against_benefit_claim", + "fieldname": "pay_against_benefit_claim", + "fieldtype": "Check", + "label": "Pay Against Benefit Claim", + "read_only": 1 + }, + { + "fetch_from": "earning_component.max_benefit_amount", + "fieldname": "max_benefit_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Benefit Amount", + "read_only": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-22 23:45:00.519134", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Benefit Application Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py similarity index 73% rename from erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py rename to erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py index 3a502fe524..65405feaf1 100644 --- a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py +++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class EmployeeBenefitApplicationDetail(Document): diff --git a/erpnext/hr/doctype/employee_other_income/__init__.py b/erpnext/payroll/doctype/employee_benefit_claim/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_other_income/__init__.py rename to erpnext/payroll/doctype/employee_benefit_claim/__init__.py diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js similarity index 77% rename from erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js rename to erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js index 5e12828ba4..6db6cb86b3 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js @@ -5,7 +5,7 @@ frappe.ui.form.on('Employee Benefit Claim', { setup: function(frm) { frm.set_query("earning_component", function() { return { - query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", + query : "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", filters: {date: frm.doc.claim_date, employee: frm.doc.employee} }; }); diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json new file mode 100644 index 0000000000..ae4c218615 --- /dev/null +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json @@ -0,0 +1,194 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "HR-BEN-CLM-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:43:10.386409", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_3", + "claim_date", + "benefit_type_and_amount", + "earning_component", + "max_amount_eligible", + "pay_against_benefit_claim", + "claimed_amount", + "salary_slip", + "amended_from", + "section_break_9", + "attachments" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "claim_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Claim Date", + "reqd": 1 + }, + { + "fieldname": "benefit_type_and_amount", + "fieldtype": "Section Break", + "label": "Benefit Type and Amount" + }, + { + "fieldname": "earning_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Claim Benefit For", + "options": "Salary Component", + "reqd": 1 + }, + { + "fetch_from": "earning_component.max_benefit_amount", + "fieldname": "max_amount_eligible", + "fieldtype": "Currency", + "label": "Max Amount Eligible", + "read_only": 1 + }, + { + "default": "0", + "fetch_from": "earning_component.pay_against_benefit_claim", + "fieldname": "pay_against_benefit_claim", + "fieldtype": "Check", + "hidden": 1, + "label": "Pay Against Benefit Claim", + "read_only": 1 + }, + { + "fieldname": "claimed_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Claimed Amount", + "reqd": 1 + }, + { + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "options": "Salary Slip", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Benefit Claim", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Expense Proof" + }, + { + "fieldname": "attachments", + "fieldtype": "Attach", + "label": "Attachments" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-06-22 23:01:50.791676", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Benefit Claim", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py similarity index 96% rename from erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py rename to erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py index 3a12c9c9f9..d9937a7bb9 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -7,10 +7,10 @@ import frappe from frappe import _ from frappe.utils import flt from frappe.model.document import Document -from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits +from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits from erpnext.hr.utils import get_previous_claimed_amount -from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period -from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure +from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period +from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure class EmployeeBenefitClaim(Document): def validate(self): diff --git a/erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js similarity index 100% rename from erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.js rename to erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js diff --git a/erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.py rename to erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/__init__.py b/erpnext/payroll/doctype/employee_incentive/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/__init__.py rename to erpnext/payroll/doctype/employee_incentive/__init__.py diff --git a/erpnext/hr/doctype/employee_incentive/employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js similarity index 100% rename from erpnext/hr/doctype/employee_incentive/employee_incentive.js rename to erpnext/payroll/doctype/employee_incentive/employee_incentive.js diff --git a/erpnext/hr/doctype/employee_incentive/employee_incentive.json b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json similarity index 97% rename from erpnext/hr/doctype/employee_incentive/employee_incentive.json rename to erpnext/payroll/doctype/employee_incentive/employee_incentive.json index e2d8a11f47..204c9a40b1 100644 --- a/erpnext/hr/doctype/employee_incentive/employee_incentive.json +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json @@ -74,9 +74,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-05 18:59:40.526014", + "modified": "2020-06-22 22:42:51.209630", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Employee Incentive", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py similarity index 100% rename from erpnext/hr/doctype/employee_incentive/employee_incentive.py rename to erpnext/payroll/doctype/employee_incentive/employee_incentive.py diff --git a/erpnext/hr/doctype/employee_incentive/test_employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js similarity index 100% rename from erpnext/hr/doctype/employee_incentive/test_employee_incentive.js rename to erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js diff --git a/erpnext/hr/doctype/employee_incentive/test_employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py similarity index 100% rename from erpnext/hr/doctype/employee_incentive/test_employee_incentive.py rename to erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/__init__.py b/erpnext/payroll/doctype/employee_other_income/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/__init__.py rename to erpnext/payroll/doctype/employee_other_income/__init__.py diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.js b/erpnext/payroll/doctype/employee_other_income/employee_other_income.js similarity index 100% rename from erpnext/hr/doctype/employee_other_income/employee_other_income.js rename to erpnext/payroll/doctype/employee_other_income/employee_other_income.js diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.json b/erpnext/payroll/doctype/employee_other_income/employee_other_income.json similarity index 97% rename from erpnext/hr/doctype/employee_other_income/employee_other_income.json rename to erpnext/payroll/doctype/employee_other_income/employee_other_income.json index 8abfe1e93a..14f63e4fdd 100644 --- a/erpnext/hr/doctype/employee_other_income/employee_other_income.json +++ b/erpnext/payroll/doctype/employee_other_income/employee_other_income.json @@ -76,9 +76,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-14 17:17:38.883126", + "modified": "2020-06-22 22:55:17.604688", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Employee Other Income", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/employee_other_income.py similarity index 100% rename from erpnext/hr/doctype/employee_other_income/employee_other_income.py rename to erpnext/payroll/doctype/employee_other_income/employee_other_income.py diff --git a/erpnext/hr/doctype/employee_other_income/test_employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py similarity index 100% rename from erpnext/hr/doctype/employee_other_income/test_employee_other_income.py rename to erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_category/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration_category/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_category/__init__.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js rename to erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json new file mode 100644 index 0000000000..f2556d7d96 --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json @@ -0,0 +1,74 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-04-13 16:51:36.971140", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "max_amount", + "is_active" + ], + "fields": [ + { + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Exemption Amount" + }, + { + "default": "1", + "fieldname": "is_active", + "fieldtype": "Check", + "label": "Is Active" + } + ], + "links": [], + "modified": "2020-06-22 23:16:47.472910", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Category", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js rename to erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/__init__.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js similarity index 88% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js index a827eca1c4..0e0c9b5a1a 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js @@ -42,7 +42,7 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', { if(frm.doc.docstatus==1) { frm.add_custom_button(__('Submit Proof'), function() { frappe.model.open_mapped_doc({ - method: "erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + method: "erpnext.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", frm: frm }); }).addClass("btn-primary"); diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json similarity index 98% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index 18fad85c4b..de7c348bb2 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -107,9 +107,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-18 14:56:25.625717", + "modified": "2020-06-22 22:49:43.829892", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Employee Tax Exemption Declaration", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json new file mode 100644 index 0000000000..8c2f9aa370 --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json @@ -0,0 +1,61 @@ +{ + "actions": [], + "creation": "2018-04-13 16:56:23.333041", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "exemption_sub_category", + "exemption_category", + "max_amount", + "amount" + ], + "fields": [ + { + "fieldname": "exemption_sub_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Exemption Sub Category", + "options": "Employee Tax Exemption Sub Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.exemption_category", + "fieldname": "exemption_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Exemption Category", + "options": "Employee Tax Exemption Category", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.max_amount", + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Maximum Exempted Amount", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Declared Amount", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-22 23:41:03.638739", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Declaration Category", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py similarity index 73% rename from erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py index 362677e887..bff747f90d 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class EmployeeTaxExemptionDeclarationCategory(Document): diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js similarity index 90% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js index 66118c0811..715d7553b0 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js @@ -43,7 +43,7 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', { frm.add_custom_button(__('Get Details From Declaration'), function() { erpnext.utils.map_current_doc({ - method: "erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + method: "erpnext.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", source_doctype: "Employee Tax Exemption Declaration", target: frm, date_field: "creation", diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json similarity index 98% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json index 8b117a25b5..b62b5aab0b 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json @@ -130,9 +130,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-18 14:55:51.420016", + "modified": "2020-06-22 22:53:10.412321", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Employee Tax Exemption Proof Submission", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py new file mode 100644 index 0000000000..cb9ed5f971 --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_exemption_category, create_payroll_period + +class TestEmployeeTaxExemptionProofSubmission(unittest.TestCase): + def setup(self): + make_employee("employee@proofsubmission.com") + create_payroll_period() + create_exemption_category() + frappe.db.sql("""delete from `tabEmployee Tax Exemption Proof Submission`""") + + def test_exemption_amount_lesser_than_category_max(self): + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Proof Submission", + "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), + "payroll_period": "Test Payroll Period", + "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", + type_of_proof = "Test Proof", + exemption_category = "_Test Category", + amount = 150000)] + }) + self.assertRaises(frappe.ValidationError, declaration.save) + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Proof Submission", + "payroll_period": "Test Payroll Period", + "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), + "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", + type_of_proof = "Test Proof", + exemption_category = "_Test Category", + amount = 100000)] + }) + self.assertTrue(declaration.save) + self.assertTrue(declaration.submit) + + def test_duplicate_category_in_proof_submission(self): + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Proof Submission", + "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), + "payroll_period": "Test Payroll Period", + "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + type_of_proof = "Test Proof", + amount = 100000), + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 50000), + ] + }) + self.assertRaises(frappe.ValidationError, declaration.save) diff --git a/erpnext/hr/doctype/income_tax_slab/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json new file mode 100644 index 0000000000..c1f532050a --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json @@ -0,0 +1,66 @@ +{ + "actions": [], + "creation": "2018-04-13 17:19:03.006149", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "exemption_sub_category", + "exemption_category", + "max_amount", + "type_of_proof", + "amount" + ], + "fields": [ + { + "fieldname": "exemption_sub_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Exemption Sub Category", + "options": "Employee Tax Exemption Sub Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.exemption_category", + "fieldname": "exemption_category", + "fieldtype": "Read Only", + "in_list_view": 1, + "label": "Exemption Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.max_amount", + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Maximum Exemption Amount", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "type_of_proof", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Type of Proof", + "reqd": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Actual Amount" + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-22 23:37:08.265600", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Proof Submission Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py similarity index 74% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py index c5d1a8fe5d..0244ae6646 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class EmployeeTaxExemptionProofSubmissionDetail(Document): diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/__init__.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/__init__.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json new file mode 100644 index 0000000000..f8c4b8bcb0 --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json @@ -0,0 +1,86 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-05-09 12:47:26.983095", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "exemption_category", + "max_amount", + "is_active" + ], + "fields": [ + { + "fieldname": "exemption_category", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Tax Exemption Category", + "options": "Employee Tax Exemption Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_category.max_amount", + "fetch_if_empty": 1, + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Exemption Amount" + }, + { + "default": "1", + "fieldname": "is_active", + "fieldtype": "Check", + "label": "Is Active" + } + ], + "links": [], + "modified": "2020-06-22 23:18:08.254645", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Sub Category", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py diff --git a/erpnext/hr/doctype/payroll_employee_detail/__init__.py b/erpnext/payroll/doctype/income_tax_slab/__init__.py similarity index 100% rename from erpnext/hr/doctype/payroll_employee_detail/__init__.py rename to erpnext/payroll/doctype/income_tax_slab/__init__.py diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.js b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js similarity index 100% rename from erpnext/hr/doctype/income_tax_slab/income_tax_slab.js rename to erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json similarity index 97% rename from erpnext/hr/doctype/income_tax_slab/income_tax_slab.json rename to erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json index f74315f32e..6337d5a6d3 100644 --- a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json +++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json @@ -94,9 +94,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-29 15:08:21.436120", + "modified": "2020-06-22 20:27:13.425084", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Income Tax Slab", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab/income_tax_slab.py rename to erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py diff --git a/erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py rename to erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py diff --git a/erpnext/hr/doctype/payroll_entry/__init__.py b/erpnext/payroll/doctype/income_tax_slab_other_charges/__init__.py similarity index 100% rename from erpnext/hr/doctype/payroll_entry/__init__.py rename to erpnext/payroll/doctype/income_tax_slab_other_charges/__init__.py diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json similarity index 95% rename from erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json rename to erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json index b23fb3dc31..7f21204591 100644 --- a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json +++ b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json @@ -14,18 +14,6 @@ "max_taxable_income" ], "fields": [ - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "columns": 2, - "fieldname": "min_taxable_income", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Min Taxable Income", - "options": "Company:company:default_currency" - }, { "columns": 4, "fieldname": "description", @@ -34,6 +22,10 @@ "label": "Description", "reqd": 1 }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { "columns": 2, "fieldname": "percent", @@ -47,6 +39,14 @@ "fieldtype": "Section Break", "label": "Conditions" }, + { + "columns": 2, + "fieldname": "min_taxable_income", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Min Taxable Income", + "options": "Company:company:default_currency" + }, { "fieldname": "column_break_7", "fieldtype": "Column Break" @@ -62,9 +62,9 @@ ], "istable": 1, "links": [], - "modified": "2020-04-24 13:27:43.598967", + "modified": "2020-06-22 23:33:17.931912", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Income Tax Slab Other Charges", "owner": "Administrator", "permissions": [], diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py rename to erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py diff --git a/erpnext/hr/doctype/payroll_period/__init__.py b/erpnext/payroll/doctype/payroll_employee_detail/__init__.py similarity index 100% rename from erpnext/hr/doctype/payroll_period/__init__.py rename to erpnext/payroll/doctype/payroll_employee_detail/__init__.py diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json new file mode 100644 index 0000000000..bb68e1814a --- /dev/null +++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json @@ -0,0 +1,66 @@ +{ + "actions": [], + "creation": "2017-11-30 06:07:33.477781", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "column_break_3", + "department", + "designation" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "read_only": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Designation", + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-22 23:25:13.779032", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Employee Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.py b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py similarity index 100% rename from erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.py rename to erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py diff --git a/erpnext/hr/doctype/payroll_period_date/__init__.py b/erpnext/payroll/doctype/payroll_entry/__init__.py similarity index 100% rename from erpnext/hr/doctype/payroll_period_date/__init__.py rename to erpnext/payroll/doctype/payroll_entry/__init__.py diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js similarity index 95% rename from erpnext/hr/doctype/payroll_entry/payroll_entry.js rename to erpnext/payroll/doctype/payroll_entry/payroll_entry.js index da25d7574e..1ae3553b9b 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -84,7 +84,7 @@ frappe.ui.form.on('Payroll Entry', { add_bank_entry_button: function(frm) { frappe.call({ - method: 'erpnext.hr.doctype.payroll_entry.payroll_entry.payroll_entry_has_bank_entries', + method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.payroll_entry_has_bank_entries', args: { 'name': frm.doc.name }, @@ -170,7 +170,7 @@ frappe.ui.form.on('Payroll Entry', { set_start_end_dates: function (frm) { if (!frm.doc.salary_slip_based_on_timesheet) { frappe.call({ - method: 'erpnext.hr.doctype.payroll_entry.payroll_entry.get_start_end_dates', + method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_start_end_dates', args: { payroll_frequency: frm.doc.payroll_frequency, start_date: frm.doc.posting_date @@ -188,7 +188,7 @@ frappe.ui.form.on('Payroll Entry', { set_end_date: function(frm){ frappe.call({ - method: 'erpnext.hr.doctype.payroll_entry.payroll_entry.get_end_date', + method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date', args: { frequency: frm.doc.payroll_frequency, start_date: frm.doc.start_date diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.json b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json similarity index 98% rename from erpnext/hr/doctype/payroll_entry/payroll_entry.json rename to erpnext/payroll/doctype/payroll_entry/payroll_entry.json index 9356f3e7b3..31a899699d 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.json +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_copy": 1, "autoname": "HR-PRUN-.YYYY.-.#####", "creation": "2017-10-23 15:22:29.291323", @@ -260,9 +261,10 @@ ], "icon": "fa fa-cog", "is_submittable": 1, - "modified": "2019-09-12 15:46:31.436381", + "links": [], + "modified": "2020-06-22 20:06:06.953904", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Payroll Entry", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py similarity index 99% rename from erpnext/hr/doctype/payroll_entry/payroll_entry.py rename to erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 656de0170d..e6bb708e41 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -55,7 +55,7 @@ class PayrollEntry(Document): ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s {condition}""".format(condition=condition), {"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet}) - + if sal_struct: cond += "and t2.salary_structure IN %(sal_struct)s " cond += "and %(from_date)s >= t2.from_date" @@ -154,7 +154,7 @@ class PayrollEntry(Document): submit_salary_slips_for_employees(self, ss_list, publish_progress=False) def email_salary_slip(self, submitted_ss): - if frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee"): + if frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee"): for ss in submitted_ss: ss.email_salary_slip() @@ -170,7 +170,7 @@ class PayrollEntry(Document): def get_salary_components(self, component_type): salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True) - if salary_slips: + if salary_slips: salary_components = frappe.db.sql(""" select ssd.salary_component, ssd.amount, ssd.parentfield, ss.payroll_cost_center from `tabSalary Slip` ss, `tabSalary Detail` ssd @@ -197,7 +197,7 @@ class PayrollEntry(Document): return account_details def get_account(self, component_dict = None): - account_dict = {} + account_dict = {} for key, amount in component_dict.items(): account = self.get_salary_component_account(key[0]) account_dict[(account, key[1])] = account_dict.get((account, key[1]), 0) + amount diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry_dashboard.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py similarity index 100% rename from erpnext/hr/doctype/payroll_entry/payroll_entry_dashboard.py rename to erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js similarity index 100% rename from erpnext/hr/doctype/payroll_entry/test_payroll_entry.js rename to erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py similarity index 96% rename from erpnext/hr/doctype/payroll_entry/test_payroll_entry.py rename to erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py index 3c318e78a2..b0f225d909 100644 --- a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -7,11 +7,11 @@ import frappe from dateutil.relativedelta import relativedelta from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate from frappe.utils import add_months -from erpnext.hr.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date +from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.salary_slip.test_salary_slip import get_salary_component_account, \ +from erpnext.payroll.doctype.salary_slip.test_salary_slip import get_salary_component_account, \ make_earning_salary_component, make_deduction_salary_component, create_account -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans @@ -24,7 +24,7 @@ class TestPayrollEntry(unittest.TestCase): make_earning_salary_component(setup=True, company_list=["_Test Company"]) make_deduction_salary_component(setup=True, company_list=["_Test Company"]) - frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0) + frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0) def test_payroll_entry(self): # pylint: disable=no-self-use company = erpnext.get_default_company() diff --git a/erpnext/hr/doctype/payroll_entry/test_set_salary_components.js b/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js similarity index 100% rename from erpnext/hr/doctype/payroll_entry/test_set_salary_components.js rename to erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js diff --git a/erpnext/hr/doctype/retention_bonus/__init__.py b/erpnext/payroll/doctype/payroll_period/__init__.py similarity index 100% rename from erpnext/hr/doctype/retention_bonus/__init__.py rename to erpnext/payroll/doctype/payroll_period/__init__.py diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.js b/erpnext/payroll/doctype/payroll_period/payroll_period.js similarity index 100% rename from erpnext/hr/doctype/payroll_period/payroll_period.js rename to erpnext/payroll/doctype/payroll_period/payroll_period.js diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.json b/erpnext/payroll/doctype/payroll_period/payroll_period.json similarity index 95% rename from erpnext/hr/doctype/payroll_period/payroll_period.json rename to erpnext/payroll/doctype/payroll_period/payroll_period.json index c0fa506e7f..0e0948475c 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.json +++ b/erpnext/payroll/doctype/payroll_period/payroll_period.json @@ -53,9 +53,9 @@ } ], "links": [], - "modified": "2020-03-18 18:13:23.859980", + "modified": "2020-06-29 17:17:12.689089", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Payroll Period", "owner": "Administrator", "permissions": [ @@ -96,6 +96,7 @@ "write": 1 } ], + "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.py b/erpnext/payroll/doctype/payroll_period/payroll_period.py similarity index 97% rename from erpnext/hr/doctype/payroll_period/payroll_period.py rename to erpnext/payroll/doctype/payroll_period/payroll_period.py index 6956c38285..d7893d0657 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.py +++ b/erpnext/payroll/doctype/payroll_period/payroll_period.py @@ -64,7 +64,7 @@ def get_payroll_period_days(start_date, end_date, employee, company=None): if len(payroll_period) > 0: actual_no_of_days = date_diff(getdate(payroll_period[0][2]), getdate(payroll_period[0][1])) + 1 working_days = actual_no_of_days - if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")): + if not cint(frappe.db.get_value("Payroll Settings", None, "include_holidays_in_total_working_days")): holidays = get_holidays_for_employee(employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2])) working_days -= len(holidays) return payroll_period[0][0], working_days, actual_no_of_days diff --git a/erpnext/hr/doctype/payroll_period/payroll_period_dashboard.py b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py similarity index 100% rename from erpnext/hr/doctype/payroll_period/payroll_period_dashboard.py rename to erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py diff --git a/erpnext/hr/doctype/payroll_period/test_payroll_period.js b/erpnext/payroll/doctype/payroll_period/test_payroll_period.js similarity index 100% rename from erpnext/hr/doctype/payroll_period/test_payroll_period.js rename to erpnext/payroll/doctype/payroll_period/test_payroll_period.js diff --git a/erpnext/hr/doctype/payroll_period/test_payroll_period.py b/erpnext/payroll/doctype/payroll_period/test_payroll_period.py similarity index 100% rename from erpnext/hr/doctype/payroll_period/test_payroll_period.py rename to erpnext/payroll/doctype/payroll_period/test_payroll_period.py diff --git a/erpnext/hr/doctype/salary_component/__init__.py b/erpnext/payroll/doctype/payroll_period_date/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_component/__init__.py rename to erpnext/payroll/doctype/payroll_period_date/__init__.py diff --git a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json new file mode 100644 index 0000000000..4a2f383b37 --- /dev/null +++ b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json @@ -0,0 +1,39 @@ +{ + "actions": [], + "creation": "2018-04-13 15:17:30.513630", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "start_date", + "end_date" + ], + "fields": [ + { + "fieldname": "start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Start Date", + "reqd": 1 + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "End Date", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-22 23:30:15.943356", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Period Date", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/payroll_period_date/payroll_period_date.py b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py similarity index 71% rename from erpnext/hr/doctype/payroll_period_date/payroll_period_date.py rename to erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py index 06ecb495f3..a3ee269d8e 100644 --- a/erpnext/hr/doctype/payroll_period_date/payroll_period_date.py +++ b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class PayrollPeriodDate(Document): diff --git a/erpnext/hr/doctype/salary_detail/__init__.py b/erpnext/payroll/doctype/payroll_settings/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_detail/__init__.py rename to erpnext/payroll/doctype/payroll_settings/__init__.py diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.js b/erpnext/payroll/doctype/payroll_settings/payroll_settings.js new file mode 100644 index 0000000000..941464dc51 --- /dev/null +++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Payroll Settings', { + encrypt_salary_slips_in_emails: function(frm) { + let encrypt_state = frm.doc.encrypt_salary_slips_in_emails; + frm.set_df_property('password_policy', 'reqd', encrypt_state); + }, + + validate: function(frm) { + let policy = frm.doc.password_policy; + if (policy) { + if (policy.includes(' ') || policy.includes('--')) { + frappe.msgprint(__("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically")); + } + frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); + } + }, +}); diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json new file mode 100644 index 0000000000..c47caa1227 --- /dev/null +++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json @@ -0,0 +1,130 @@ +{ + "actions": [], + "creation": "2020-06-04 15:13:33.589685", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "payroll_based_on", + "consider_unmarked_attendance_as", + "max_working_hours_against_timesheet", + "include_holidays_in_total_working_days", + "disable_rounded_total", + "column_break_11", + "daily_wages_fraction_for_half_day", + "email_salary_slip_to_employee", + "encrypt_salary_slips_in_emails", + "password_policy" + ], + "fields": [ + { + "default": "Leave", + "fieldname": "payroll_based_on", + "fieldtype": "Select", + "label": "Calculate Payroll Working Days Based On", + "options": "Leave\nAttendance", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "max_working_hours_against_timesheet", + "fieldtype": "Float", + "label": "Max working hours against Timesheet", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day", + "fieldname": "include_holidays_in_total_working_days", + "fieldtype": "Check", + "label": "Include holidays in Total no. of Working Days", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "description": "If checked, hides and disables Rounded Total field in Salary Slips", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0.5", + "description": "The fraction of daily wages to be paid for half-day attendance", + "fieldname": "daily_wages_fraction_for_half_day", + "fieldtype": "Float", + "label": "Fraction of Daily Salary for Half Day", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "1", + "description": "Emails salary slip to employee based on preferred email selected in Employee", + "fieldname": "email_salary_slip_to_employee", + "fieldtype": "Check", + "label": "Email Salary Slip to Employee", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "depends_on": "eval: doc.email_salary_slip_to_employee == 1;", + "description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.", + "fieldname": "encrypt_salary_slips_in_emails", + "fieldtype": "Check", + "label": "Encrypt Salary Slips in Emails", + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1", + "description": "Example: SAL-{first_name}-{date_of_birth.year}
This will generate a password like SAL-Jane-1972", + "fieldname": "password_policy", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Password Policy", + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval:doc.payroll_based_on == 'Attendance'", + "fieldname": "consider_unmarked_attendance_as", + "fieldtype": "Select", + "label": "Consider Unmarked Attendance As", + "options": "Present\nAbsent", + "show_days": 1, + "show_seconds": 1 + } + ], + "icon": "fa fa-cog", + "issingle": 1, + "links": [], + "modified": "2020-06-22 17:00:58.408030", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC" +} \ No newline at end of file diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py new file mode 100644 index 0000000000..5efa41db1f --- /dev/null +++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +from frappe.utils import cint +from frappe.custom.doctype.property_setter.property_setter import make_property_setter +from frappe import _ + +class PayrollSettings(Document): + def validate(self): + self.validate_password_policy() + + if not self.daily_wages_fraction_for_half_day: + self.daily_wages_fraction_for_half_day = 0.5 + + def validate_password_policy(self): + if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails: + if not self.password_policy: + frappe.throw(_("Password policy for Salary Slips is not set")) + + + def on_update(self): + self.toggle_rounded_total() + frappe.clear_cache() + + def toggle_rounded_total(self): + self.disable_rounded_total = cint(self.disable_rounded_total) + make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check") + make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check") diff --git a/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py new file mode 100644 index 0000000000..314866e128 --- /dev/null +++ b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestPayrollSettings(unittest.TestCase): + pass diff --git a/erpnext/hr/doctype/salary_slip_timesheet/__init__.py b/erpnext/payroll/doctype/retention_bonus/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_slip_timesheet/__init__.py rename to erpnext/payroll/doctype/retention_bonus/__init__.py diff --git a/erpnext/hr/doctype/retention_bonus/retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js similarity index 100% rename from erpnext/hr/doctype/retention_bonus/retention_bonus.js rename to erpnext/payroll/doctype/retention_bonus/retention_bonus.js diff --git a/erpnext/hr/doctype/retention_bonus/retention_bonus.json b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json similarity index 90% rename from erpnext/hr/doctype/retention_bonus/retention_bonus.json rename to erpnext/payroll/doctype/retention_bonus/retention_bonus.json index 7781053e13..da884c2f28 100644 --- a/erpnext/hr/doctype/retention_bonus/retention_bonus.json +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "HR-RTB-.YYYY.-.#####", @@ -16,8 +17,7 @@ "column_break_6", "employee_name", "department", - "date_of_joining", - "additional_salary" + "date_of_joining" ], "fields": [ { @@ -83,14 +83,6 @@ "label": "Date of Joining", "read_only": 1 }, - { - "fieldname": "additional_salary", - "fieldtype": "Link", - "label": "Additional Salary", - "no_copy": 1, - "options": "Additional Salary", - "read_only": 1 - }, { "fieldname": "salary_component", "fieldtype": "Link", @@ -100,9 +92,10 @@ } ], "is_submittable": 1, - "modified": "2019-09-03 16:47:24.210422", + "links": [], + "modified": "2020-06-22 22:42:05.251951", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Retention Bonus", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py similarity index 76% rename from erpnext/hr/doctype/retention_bonus/retention_bonus.py rename to erpnext/payroll/doctype/retention_bonus/retention_bonus.py index 48637a350c..ed0d36cfa5 100644 --- a/erpnext/hr/doctype/retention_bonus/retention_bonus.py +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py @@ -17,13 +17,7 @@ class RetentionBonus(Document): def on_submit(self): company = frappe.db.get_value('Employee', self.employee, 'company') - additional_salary = frappe.db.exists('Additional Salary', { - 'employee': self.employee, - 'salary_component': self.salary_component, - 'payroll_date': self.bonus_payment_date, - 'company': company, - 'docstatus': 1 - }) + additional_salary = self.get_additional_salary() if not additional_salary: additional_salary = frappe.new_doc('Additional Salary') @@ -32,8 +26,10 @@ class RetentionBonus(Document): additional_salary.amount = self.bonus_amount additional_salary.payroll_date = self.bonus_payment_date additional_salary.company = company + additional_salary.ref_doctype = self.doctype + additional_salary.ref_docname = self.name additional_salary.submit() - self.db_set('additional_salary', additional_salary.name) + # self.db_set('additional_salary', additional_salary.name) else: bonus_added = frappe.db.get_value('Additional Salary', additional_salary, 'amount') + self.bonus_amount @@ -41,11 +37,24 @@ class RetentionBonus(Document): self.db_set('additional_salary', additional_salary) def on_cancel(self): + + additional_salary = self.get_additional_salary() if self.additional_salary: bonus_removed = frappe.db.get_value('Additional Salary', self.additional_salary, 'amount') - self.bonus_amount if bonus_removed == 0: frappe.get_doc('Additional Salary', self.additional_salary).cancel() else: frappe.db.set_value('Additional Salary', self.additional_salary, 'amount', bonus_removed) - - self.db_set('additional_salary', '') \ No newline at end of file + + # self.db_set('additional_salary', '') + + def get_additional_salary(self): + return frappe.db.exists('Additional Salary', { + 'employee': self.employee, + 'salary_component': self.salary_component, + 'payroll_date': self.bonus_payment_date, + 'company': company, + 'docstatus': 1, + 'ref_doctype': self.doctype, + 'ref_docname': self.name + }) diff --git a/erpnext/hr/doctype/retention_bonus/test_retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js similarity index 100% rename from erpnext/hr/doctype/retention_bonus/test_retention_bonus.js rename to erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js diff --git a/erpnext/hr/doctype/retention_bonus/test_retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py similarity index 100% rename from erpnext/hr/doctype/retention_bonus/test_retention_bonus.py rename to erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py diff --git a/erpnext/hr/doctype/salary_component/README.md b/erpnext/payroll/doctype/salary_component/README.md similarity index 100% rename from erpnext/hr/doctype/salary_component/README.md rename to erpnext/payroll/doctype/salary_component/README.md diff --git a/erpnext/hr/doctype/salary_structure_assignment/__init__.py b/erpnext/payroll/doctype/salary_component/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/__init__.py rename to erpnext/payroll/doctype/salary_component/__init__.py diff --git a/erpnext/hr/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js similarity index 100% rename from erpnext/hr/doctype/salary_component/salary_component.js rename to erpnext/payroll/doctype/salary_component/salary_component.js diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/payroll/doctype/salary_component/salary_component.json similarity index 95% rename from erpnext/hr/doctype/salary_component/salary_component.json rename to erpnext/payroll/doctype/salary_component/salary_component.json index 97c46c829e..225b048293 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.json +++ b/erpnext/payroll/doctype/salary_component/salary_component.json @@ -16,6 +16,7 @@ "column_break_4", "depends_on_payment_days", "is_tax_applicable", + "is_income_tax_component", "deduct_full_tax_on_selected_payroll_date", "variable_based_on_taxable_salary", "exempted_from_income_tax", @@ -231,13 +232,22 @@ "fieldname": "exempted_from_income_tax", "fieldtype": "Check", "label": "Exempted from Income Tax" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Deduction\"", + "fieldname": "is_income_tax_component", + "fieldtype": "Check", + "label": "Is Income Tax Component", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-flag", "links": [], - "modified": "2020-04-28 15:46:45.252945", + "modified": "2020-06-22 15:39:20.826565", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Component", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/salary_component/salary_component.py b/erpnext/payroll/doctype/salary_component/salary_component.py similarity index 100% rename from erpnext/hr/doctype/salary_component/salary_component.py rename to erpnext/payroll/doctype/salary_component/salary_component.py diff --git a/erpnext/hr/doctype/salary_component/test_records.json b/erpnext/payroll/doctype/salary_component/test_records.json similarity index 100% rename from erpnext/hr/doctype/salary_component/test_records.json rename to erpnext/payroll/doctype/salary_component/test_records.json diff --git a/erpnext/hr/doctype/salary_component/test_salary_component.js b/erpnext/payroll/doctype/salary_component/test_salary_component.js similarity index 100% rename from erpnext/hr/doctype/salary_component/test_salary_component.js rename to erpnext/payroll/doctype/salary_component/test_salary_component.js diff --git a/erpnext/hr/doctype/salary_component/test_salary_component.py b/erpnext/payroll/doctype/salary_component/test_salary_component.py similarity index 100% rename from erpnext/hr/doctype/salary_component/test_salary_component.py rename to erpnext/payroll/doctype/salary_component/test_salary_component.py diff --git a/erpnext/hr/doctype/taxable_salary_slab/__init__.py b/erpnext/payroll/doctype/salary_detail/__init__.py similarity index 100% rename from erpnext/hr/doctype/taxable_salary_slab/__init__.py rename to erpnext/payroll/doctype/salary_detail/__init__.py diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json similarity index 99% rename from erpnext/hr/doctype/salary_detail/salary_detail.json rename to erpnext/payroll/doctype/salary_detail/salary_detail.json index fe5f83b532..adb54f26c6 100644 --- a/erpnext/hr/doctype/salary_detail/salary_detail.json +++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json @@ -211,13 +211,13 @@ ], "istable": 1, "links": [], - "modified": "2020-04-04 20:00:16.475295", + "modified": "2020-06-22 23:21:26.300951", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Detail", "owner": "Administrator", "permissions": [], "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.py b/erpnext/payroll/doctype/salary_detail/salary_detail.py similarity index 100% rename from erpnext/hr/doctype/salary_detail/salary_detail.py rename to erpnext/payroll/doctype/salary_detail/salary_detail.py diff --git a/erpnext/hr/doctype/salary_slip/README.md b/erpnext/payroll/doctype/salary_slip/README.md similarity index 100% rename from erpnext/hr/doctype/salary_slip/README.md rename to erpnext/payroll/doctype/salary_slip/README.md diff --git a/erpnext/hr/doctype/salary_slip/__init__.py b/erpnext/payroll/doctype/salary_slip/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_slip/__init__.py rename to erpnext/payroll/doctype/salary_slip/__init__.py diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js similarity index 93% rename from erpnext/hr/doctype/salary_slip/salary_slip.js rename to erpnext/payroll/doctype/salary_slip/salary_slip.js index 1c4d4e34c5..4b623e57b4 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js @@ -56,7 +56,7 @@ frappe.ui.form.on("Salary Slip", { set_end_date: function(frm){ frappe.call({ - method: 'erpnext.hr.doctype.payroll_entry.payroll_entry.get_end_date', + method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date', args: { frequency: frm.doc.payroll_frequency, start_date: frm.doc.start_date @@ -123,6 +123,9 @@ frappe.ui.form.on("Salary Slip", { doc: frm.doc, callback: function(r, rt) { frm.refresh(); + if (frm.doc.absent_days){ + frm.fields_dict.absent_days.set_description("Unmarked Days is treated as "+ r.message +". You can can change this in " + frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true)); + } } }); } diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json similarity index 70% rename from erpnext/hr/doctype/salary_slip/salary_slip.json rename to erpnext/payroll/doctype/salary_slip/salary_slip.json index cfd4d89731..663a3ef9ea 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -23,11 +23,13 @@ "salary_slip_based_on_timesheet", "start_date", "end_date", - "column_break_15", "salary_structure", "payroll_frequency", + "column_break_15", "total_working_days", + "unmarked_days", "leave_without_pay", + "absent_days", "payment_days", "hourly_wages", "timesheets", @@ -37,6 +39,7 @@ "section_break_26", "bank_name", "bank_account_no", + "mode_of_payment", "section_break_32", "deduct_tax_for_unclaimed_employee_benefits", "deduct_tax_for_unsubmitted_tax_exemption_proof", @@ -71,7 +74,9 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Posting Date", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "employee", @@ -84,7 +89,9 @@ "oldfieldtype": "Link", "options": "Employee", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "employee.employee_name", @@ -95,7 +102,9 @@ "label": "Employee Name", "oldfieldname": "employee_name", "oldfieldtype": "Data", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "employee.department", @@ -106,7 +115,9 @@ "oldfieldname": "department", "oldfieldtype": "Link", "options": "Department", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.designation", @@ -115,7 +126,9 @@ "fieldtype": "Read Only", "label": "Designation", "oldfieldname": "designation", - "oldfieldtype": "Link" + "oldfieldtype": "Link", + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "employee.branch", @@ -126,12 +139,16 @@ "oldfieldname": "branch", "oldfieldtype": "Link", "options": "Branch", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -139,21 +156,27 @@ "fieldtype": "Select", "label": "Status", "options": "Draft\nSubmitted\nCancelled", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "journal_entry", "fieldtype": "Link", "label": "Journal Entry", "options": "Journal Entry", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payroll_entry", "fieldtype": "Link", "label": "Payroll Entry", "options": "Payroll Entry", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -163,7 +186,9 @@ "label": "Company", "options": "Company", "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -172,46 +197,62 @@ "ignore_user_permissions": 1, "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_10", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "salary_slip_based_on_timesheet", "fieldtype": "Check", "label": "Salary Slip Based on Timesheet", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "start_date", "fieldtype": "Date", - "label": "Start Date" + "label": "Start Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "end_date", "fieldtype": "Date", - "label": "End Date" + "label": "End Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "salary_structure", "fieldtype": "Link", "label": "Salary Structure", "options": "Salary Structure", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", "fieldname": "payroll_frequency", "fieldtype": "Select", "label": "Payroll Frequency", - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_working_days", @@ -220,14 +261,18 @@ "oldfieldname": "total_days_in_month", "oldfieldtype": "Int", "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "leave_without_pay", "fieldtype": "Float", "label": "Leave Without Pay", "oldfieldname": "leave_without_pay", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_days", @@ -236,38 +281,52 @@ "oldfieldname": "payment_days", "oldfieldtype": "Float", "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "hourly_wages", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "timesheets", "fieldtype": "Table", "label": "Salary Slip Timesheet", - "options": "Salary Slip Timesheet" + "options": "Salary Slip Timesheet", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_20", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_working_hours", "fieldtype": "Float", "label": "Total Working Hours", - "print_hide_if_no_value": 1 + "print_hide_if_no_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "hour_rate", "fieldtype": "Currency", "label": "Hour Rate", "options": "Company:company:default_currency", - "print_hide_if_no_value": 1 + "print_hide_if_no_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_26", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bank_name", @@ -275,7 +334,9 @@ "label": "Bank Name", "oldfieldname": "bank_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bank_account_no", @@ -283,35 +344,47 @@ "label": "Bank Account No.", "oldfieldname": "bank_account_no", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_32", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "deduct_tax_for_unclaimed_employee_benefits", "fieldtype": "Check", - "label": "Deduct Tax For Unclaimed Employee Benefits" + "label": "Deduct Tax For Unclaimed Employee Benefits", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof", "fieldtype": "Check", - "label": "Deduct Tax For Unsubmitted Tax Exemption Proof" + "label": "Deduct Tax For Unsubmitted Tax Exemption Proof", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "earning_deduction", "fieldtype": "Section Break", "label": "Earning & Deduction", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "earning", "fieldtype": "Column Break", "label": "Earning", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -320,13 +393,17 @@ "label": "Earnings", "oldfieldname": "earning_details", "oldfieldtype": "Table", - "options": "Salary Detail" + "options": "Salary Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "deduction", "fieldtype": "Column Break", "label": "Deduction", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -335,12 +412,16 @@ "label": "Deductions", "oldfieldname": "deduction_details", "oldfieldtype": "Table", - "options": "Salary Detail" + "options": "Salary Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "gross_pay", @@ -349,11 +430,15 @@ "oldfieldname": "gross_pay", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_25", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_deduction", @@ -362,24 +447,32 @@ "oldfieldname": "total_deduction", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "total_loan_repayment", "fieldname": "loan_repayment", "fieldtype": "Section Break", - "label": "Loan repayment" + "label": "Loan repayment", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "loans", "fieldtype": "Table", "label": "Employee Loan", "options": "Salary Slip Loan", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_43", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -387,7 +480,9 @@ "fieldtype": "Currency", "label": "Total Principal Amount", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -395,11 +490,15 @@ "fieldtype": "Currency", "label": "Total Interest Amount", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_45", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -407,12 +506,16 @@ "fieldtype": "Currency", "label": "Total Loan Repayment", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_pay_info", "fieldtype": "Section Break", - "label": "net pay info" + "label": "net pay info", + "show_days": 1, + "show_seconds": 1 }, { "description": "Gross Pay - Total Deduction - Loan Repayment", @@ -422,11 +525,15 @@ "oldfieldname": "net_pay", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_53", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -434,11 +541,15 @@ "fieldtype": "Currency", "label": "Rounded Total", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_55", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Net Pay (in words) will be visible once you save the Salary Slip.", @@ -447,7 +558,9 @@ "label": "Total in words", "oldfieldname": "net_pay_in_words", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -459,7 +572,9 @@ "oldfieldtype": "Data", "options": "Salary Slip", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "employee.payroll_cost_center", @@ -468,16 +583,42 @@ "fieldtype": "Link", "label": "Payroll Cost Center", "options": "Cost Center", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "mode_of_payment", + "fieldtype": "Select", + "label": "Mode Of Payment", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "absent_days", + "fieldtype": "Float", + "label": "Absent Days", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "unmarked_days", + "fieldtype": "Float", + "hidden": 1, + "label": "Unmarked days", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-05-05 18:55:26.173629", + "modified": "2020-06-22 14:42:43.921828", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Slip", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py similarity index 91% rename from erpnext/hr/doctype/salary_slip/salary_slip.py rename to erpnext/payroll/doctype/salary_slip/salary_slip.py index 4d5c8437c6..1e2983e421 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -9,14 +9,14 @@ from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, from frappe.model.naming import make_autoname from frappe import msgprint, _ -from erpnext.hr.doctype.payroll_entry.payroll_entry import get_start_end_dates +from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.utilities.transaction_base import TransactionBase from frappe.utils.background_jobs import enqueue -from erpnext.hr.doctype.additional_salary.additional_salary import get_additional_salary_component -from erpnext.hr.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period -from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount -from erpnext.hr.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits +from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salary_component +from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period +from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount +from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry class SalarySlip(TransactionBase): @@ -54,8 +54,8 @@ class SalarySlip(TransactionBase): total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total self.total_in_words = money_in_words(total, company_currency) - if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"): - max_working_hours = frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet") + if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"): + max_working_hours = frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet") if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)): frappe.msgprint(_("Total working hours should not be greater than max working hours {0}"). format(max_working_hours), alert=True) @@ -67,7 +67,7 @@ class SalarySlip(TransactionBase): self.set_status() self.update_status(self.name) self.make_loan_repayment_entry() - if (frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry: + if (frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry: self.email_salary_slip() def on_cancel(self): @@ -93,7 +93,7 @@ class SalarySlip(TransactionBase): frappe.throw(_("To date cannot be before From date")) def is_rounding_total_disabled(self): - return cint(frappe.db.get_single_value("HR Settings", "disable_rounded_total")) + return cint(frappe.db.get_single_value("Payroll Settings", "disable_rounded_total")) def check_existing(self): if not self.salary_slip_based_on_timesheet: @@ -136,6 +136,8 @@ class SalarySlip(TransactionBase): self.salary_slip_based_on_timesheet = self._salary_structure_doc.salary_slip_based_on_timesheet or 0 self.set_time_sheet() self.pull_sal_struct() + consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present" + return consider_unmarked_attendance_as def set_time_sheet(self): if self.salary_slip_based_on_timesheet: @@ -175,7 +177,7 @@ class SalarySlip(TransactionBase): .format(self.employee), title=_('Salary Structure Missing')) def pull_sal_struct(self): - from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip + from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip if self.salary_slip_based_on_timesheet: self.salary_structure = self._salary_structure_doc.name @@ -188,8 +190,8 @@ class SalarySlip(TransactionBase): make_salary_slip(self._salary_structure_doc.name, self) def get_working_days_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0): - payroll_based_on = frappe.db.get_value("HR Settings", None, "payroll_based_on") - include_holidays_in_total_working_days = frappe.db.get_single_value("HR Settings", "include_holidays_in_total_working_days") + payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on") + include_holidays_in_total_working_days = frappe.db.get_single_value("Payroll Settings", "include_holidays_in_total_working_days") working_days = date_diff(self.end_date, self.start_date) + 1 if for_preview: @@ -198,17 +200,18 @@ class SalarySlip(TransactionBase): return holidays = self.get_holidays_for_employee(self.start_date, self.end_date) - + if not cint(include_holidays_in_total_working_days): working_days -= len(holidays) if working_days < 0: frappe.throw(_("There are more holidays than working days this month.")) if not payroll_based_on: - frappe.throw(_("Please set Payroll based on in HR settings")) - + frappe.throw(_("Please set Payroll based on in Payroll settings")) + if payroll_based_on == "Attendance": - actual_lwp = self.calculate_lwp_based_on_attendance(holidays) + actual_lwp, absent = self.calculate_lwp_and_absent_days_based_on_attendance(holidays) + self.absent_days = absent else: actual_lwp = self.calculate_lwp_based_on_leave_application(holidays, working_days) @@ -226,9 +229,34 @@ class SalarySlip(TransactionBase): if flt(payment_days) > flt(lwp): self.payment_days = flt(payment_days) - flt(lwp) + + if payroll_based_on == "Attendance": + self.payment_days -= flt(absent) + + unmarked_days = self.get_unmarked_days() + consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present" + + if payroll_based_on == "Attendance" and consider_unmarked_attendance_as =="Absent": + self.absent_days += unmarked_days #will be treated as absent + self.payment_days -= unmarked_days + if include_holidays_in_total_working_days: + self.absent_days -= len(holidays) + for holiday in holidays: + if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }): + self.payment_days += 1 else: self.payment_days = 0 + def get_unmarked_days(self): + marked_days = frappe.get_all("Attendance", filters = { + "attendance_date": ["between", [self.start_date, self.end_date]], + "employee": self.employee, + "docstatus": 1 + }, fields = ["COUNT(*) as marked_days"])[0].marked_days + + return self.total_working_days - marked_days + + def get_payment_days(self, joining_date, relieving_date, include_holidays_in_total_working_days): if not joining_date: joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee, @@ -277,7 +305,7 @@ class SalarySlip(TransactionBase): lwp = 0 holidays = "','".join(holidays) daily_wages_fraction_for_half_day = \ - flt(frappe.db.get_value("HR Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 + flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 for d in range(working_days): dt = add_days(cstr(getdate(self.start_date)), d) @@ -304,15 +332,15 @@ class SalarySlip(TransactionBase): lwp += (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1 return lwp - - def calculate_lwp_based_on_attendance(self, holidays): + + def calculate_lwp_and_absent_days_based_on_attendance(self, holidays): lwp = 0 + absent = 0 daily_wages_fraction_for_half_day = \ - flt(frappe.db.get_value("HR Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 + flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 lwp_leave_types = dict(frappe.get_all("Leave Type", {"is_lwp": 1}, ["name", "include_holiday"], as_list=1)) - attendances = frappe.db.sql(''' SELECT attendance_date, status, leave_type FROM `tabAttendance` @@ -322,7 +350,7 @@ class SalarySlip(TransactionBase): AND docstatus = 1 AND attendance_date between %s and %s ''', values=(self.employee, self.start_date, self.end_date), as_dict=1) - + for d in attendances: if d.status in ('Half Day', 'On Leave') and d.leave_type and d.leave_type not in lwp_leave_types: continue @@ -332,9 +360,14 @@ class SalarySlip(TransactionBase): (d.leave_type and d.leave_type in lwp_leave_types and not lwp_leave_types[d.leave_type]): continue - lwp += (1 - daily_wages_fraction_for_half_day) if d.status == "Half Day" else 1 + if d.status == "Half Day": + lwp += (1 - daily_wages_fraction_for_half_day) + elif d.status == "On Leave" and d.leave_type in lwp_leave_types: + lwp += 1 + elif d.status == "Absent": + absent += 1 - return lwp + return lwp, absent def add_earning_for_hourly_wages(self, doc, salary_component, amount): row_exists = False @@ -578,7 +611,7 @@ class SalarySlip(TransactionBase): # Total taxable earnings including additional and other incomes total_taxable_earnings = previous_taxable_earnings + current_structured_taxable_earnings + future_structured_taxable_earnings \ + current_additional_earnings + other_incomes + unclaimed_taxable_benefits - total_exemption_amount - + # Total taxable earnings without additional earnings with full tax total_taxable_earnings_without_full_tax_addl_components = total_taxable_earnings - current_additional_earnings_with_full_tax @@ -586,7 +619,7 @@ class SalarySlip(TransactionBase): total_structured_tax_amount = self.calculate_tax_by_tax_slab( total_taxable_earnings_without_full_tax_addl_components, tax_slab) current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods - + # Total taxable earnings with additional earnings with full tax full_tax_on_additional_earnings = 0.0 if current_additional_earnings_with_full_tax: @@ -622,7 +655,7 @@ class SalarySlip(TransactionBase): select sum(sd.amount) from `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name - where + where sd.parentfield='earnings' and sd.is_tax_applicable=1 and is_flexible_benefit=0 @@ -841,7 +874,7 @@ class SalarySlip(TransactionBase): if flt(d.max_taxable_income) and flt(d.max_taxable_income) < tax_amount: continue - + tax_amount += tax_amount * flt(d.percent) / 100 return tax_amount @@ -955,13 +988,13 @@ class SalarySlip(TransactionBase): def email_salary_slip(self): receiver = frappe.db.get_value("Employee", self.employee, "prefered_email") - hr_settings = frappe.get_single("HR Settings") + payroll_settings = frappe.get_single("Payroll Settings") message = "Please see attachment" password = None - if hr_settings.encrypt_salary_slips_in_emails: - password = generate_password_for_pdf(hr_settings.password_policy, self.employee) + if payroll_settings.encrypt_salary_slips_in_emails: + password = generate_password_for_pdf(payroll_settings.password_policy, self.employee) message += """
Note: Your salary slip is password protected, - the password to unlock the PDF is of the format {0}. """.format(hr_settings.password_policy) + the password to unlock the PDF is of the format {0}. """.format(payroll_settings.password_policy) if receiver: email_args = { @@ -1004,8 +1037,9 @@ class SalarySlip(TransactionBase): self.calculate_net_pay() def pull_emp_details(self): - emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no"], as_dict=1) + emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no", "salary_mode"], as_dict=1) if emp: + self.mode_of_payment = emp.salary_mode self.bank_name = emp.bank_name self.bank_account_no = emp.bank_ac_no diff --git a/erpnext/hr/doctype/salary_slip/salary_slip_list.js b/erpnext/payroll/doctype/salary_slip/salary_slip_list.js similarity index 100% rename from erpnext/hr/doctype/salary_slip/salary_slip_list.js rename to erpnext/payroll/doctype/salary_slip/salary_slip_list.js diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.js b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js similarity index 100% rename from erpnext/hr/doctype/salary_slip/test_salary_slip.js rename to erpnext/payroll/doctype/salary_slip/test_salary_slip.js diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py similarity index 94% rename from erpnext/hr/doctype/salary_slip/test_salary_slip.py rename to erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 3eff738ec8..be9a2d3728 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -10,17 +10,17 @@ import random from erpnext.accounts.utils import get_fiscal_year from frappe.utils.make_random import get_random from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_day, get_last_day -from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip -from erpnext.hr.doctype.payroll_entry.payroll_entry import get_month_details +from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip +from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration \ +from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration \ import create_payroll_period, create_exemption_category class TestSalarySlip(unittest.TestCase): def setUp(self): setup_test() def tearDown(self): - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) frappe.set_user("Administrator") def test_payment_days_based_on_attendance(self): @@ -28,8 +28,8 @@ class TestSalarySlip(unittest.TestCase): no_of_days = self.get_no_of_days() # Payroll based on attendance - frappe.db.set_value("HR Settings", None, "payroll_based_on", "Attendance") - frappe.db.set_value("HR Settings", None, "daily_wages_fraction_for_half_day", 0.75) + frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance") + frappe.db.set_value("Payroll Settings", None, "daily_wages_fraction_for_half_day", 0.75) emp_id = make_employee("test_for_attendance@salary.com") frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) @@ -47,7 +47,7 @@ class TestSalarySlip(unittest.TestCase): """, (month_start_date, month_end_date))[0][0] mark_attendance(emp_id, first_sunday, 'Absent', ignore_validate=True) # invalid lwp - mark_attendance(emp_id, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # valid lwp + mark_attendance(emp_id, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent mark_attendance(emp_id, add_days(first_sunday, 2), 'Half Day', leave_type='Leave Without Pay', ignore_validate=True) # valid 0.75 lwp mark_attendance(emp_id, add_days(first_sunday, 3), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # valid lwp mark_attendance(emp_id, add_days(first_sunday, 4), 'On Leave', leave_type='Casual Leave', ignore_validate=True) # invalid lwp @@ -55,7 +55,8 @@ class TestSalarySlip(unittest.TestCase): ss = make_employee_salary_slip("test_for_attendance@salary.com", "Monthly") - self.assertEqual(ss.leave_without_pay, 2.25) + self.assertEqual(ss.leave_without_pay, 1.25) + self.assertEqual(ss.absent_days, 1) days_in_month = no_of_days[0] no_of_holidays = no_of_days[1] @@ -63,17 +64,17 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 2.25) #Gross pay calculation based on attendances - gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay)) + gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay + ss.absent_days)) self.assertEqual(ss.gross_pay, gross_pay) - frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave") + frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") def test_payment_days_based_on_leave_application(self): no_of_days = self.get_no_of_days() # Payroll based on attendance - frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave") + frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") emp_id = make_employee("test_for_attendance@salary.com") frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) @@ -106,11 +107,11 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.gross_pay, gross_pay) - frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave") + frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") def test_salary_slip_with_holidays_included(self): no_of_days = self.get_no_of_days() - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1) make_employee("test_employee@salary.com") frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) @@ -126,7 +127,7 @@ class TestSalarySlip(unittest.TestCase): def test_salary_slip_with_holidays_excluded(self): no_of_days = self.get_no_of_days() - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) make_employee("test_employee@salary.com") frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) @@ -144,7 +145,7 @@ class TestSalarySlip(unittest.TestCase): def test_payment_days(self): no_of_days = self.get_no_of_days() # Holidays not included in working days - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1) # set joinng date in the same month make_employee("test_employee@salary.com") @@ -200,7 +201,7 @@ class TestSalarySlip(unittest.TestCase): def test_email_salary_slip(self): frappe.db.sql("delete from `tabEmail Queue`") - frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 1) + frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 1) make_employee("test_employee@salary.com") ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") @@ -270,7 +271,8 @@ class TestSalarySlip(unittest.TestCase): # as per assigned salary structure 40500 in monthly salary so 236000*5/100/12 frappe.db.sql("""delete from `tabPayroll Period`""") frappe.db.sql("""delete from `tabSalary Component`""") - + frappe.db.sql("""delete from `tabAdditional Salary`""") + payroll_period = create_payroll_period() create_tax_slab(payroll_period, allow_tax_exemption=True) @@ -287,7 +289,7 @@ class TestSalarySlip(unittest.TestCase): for doc in delete_docs: frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee)) - from erpnext.hr.doctype.salary_structure.test_salary_structure import \ + from erpnext.payroll.doctype.salary_structure.test_salary_structure import \ make_salary_structure, create_salary_structure_assignment salary_structure = make_salary_structure("Stucture to test tax", "Monthly", other_details={"max_benefits": 100000}, test_tax=True) @@ -378,7 +380,7 @@ class TestSalarySlip(unittest.TestCase): return [no_of_days_in_month[1], no_of_holidays_in_month] def make_employee_salary_slip(user, payroll_frequency, salary_structure=None): - from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure + from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure if not salary_structure: salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip" @@ -699,7 +701,7 @@ def setup_test(): make_holiday_list() frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List") - frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0) + frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0) frappe.db.set_value('HR Settings', None, 'leave_status_notification_template', None) frappe.db.set_value('HR Settings', None, 'leave_approval_notification_template', None) diff --git a/erpnext/hr/notification/retention_bonus/__init__.py b/erpnext/payroll/doctype/salary_slip_timesheet/__init__.py similarity index 100% rename from erpnext/hr/notification/retention_bonus/__init__.py rename to erpnext/payroll/doctype/salary_slip_timesheet/__init__.py diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json new file mode 100644 index 0000000000..9930c53ec9 --- /dev/null +++ b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2016-06-14 19:22:29.811658", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "time_sheet", + "working_hours" + ], + "fields": [ + { + "fieldname": "time_sheet", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Time Sheet", + "options": "Timesheet", + "reqd": 1 + }, + { + "fieldname": "working_hours", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Working Hours", + "no_copy": 1, + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-22 23:27:43.463532", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Slip Timesheet", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.py b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py similarity index 72% rename from erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.py rename to erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py index 1bbfc53a32..7adb12e83a 100644 --- a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.py +++ b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class SalarySlipTimesheet(Document): diff --git a/erpnext/hr/doctype/salary_structure/README.md b/erpnext/payroll/doctype/salary_structure/README.md similarity index 100% rename from erpnext/hr/doctype/salary_structure/README.md rename to erpnext/payroll/doctype/salary_structure/README.md diff --git a/erpnext/hr/doctype/salary_structure/__init__.py b/erpnext/payroll/doctype/salary_structure/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_structure/__init__.py rename to erpnext/payroll/doctype/salary_structure/__init__.py diff --git a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html new file mode 100644 index 0000000000..d07a1ab551 --- /dev/null +++ b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html @@ -0,0 +1,47 @@ +

Variables

+ + +

Examples for Conditions and formula

+ \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js similarity index 91% rename from erpnext/hr/doctype/salary_structure/salary_structure.js rename to erpnext/payroll/doctype/salary_structure/salary_structure.js index 7748403513..ca458f976f 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js @@ -14,7 +14,30 @@ cur_frm.cscript.onload = function(doc, dt, dn){ frappe.ui.form.on('Salary Structure', { onload: function(frm) { - frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet), + + let help_button = $(` + Condition and Formula Help + `).click(()=>{ + + let d = new frappe.ui.Dialog({ + title: 'Condition and Formula Help', + fields: [ + { + fieldname: 'msg_wrapper', + fieldtype: 'HTML' + } + ] + }); + + let message_html = frappe.render_template("condition_and_formula_help") + + d.fields_dict.msg_wrapper.$wrapper.append(message_html) + + d.show() + }); + frm.get_field("conditions_and_formula_variable_and_example").$wrapper.append(frm.doc.filters_html).append(help_button) + + frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet) frm.set_query("salary_component", "earnings", function() { return { @@ -114,7 +137,7 @@ frappe.ui.form.on('Salary Structure', { preview_salary_slip: function(frm) { frappe.call({ - method: "erpnext.hr.doctype.salary_structure.salary_structure.get_employees", + method: "erpnext.payroll.doctype.salary_structure.salary_structure.get_employees", args: { salary_structure: frm.doc.name }, @@ -155,7 +178,7 @@ frappe.ui.form.on('Salary Structure', { open_salary_slip: function(frm, employee){ var print_format = frm.doc.salary_slip_based_on_timesheet ? "Salary Slip based on Timesheet" : "Salary Slip Standard"; frappe.call({ - method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip", + method: "erpnext.payroll.doctype.salary_structure.salary_structure.make_salary_slip", args: { source_name: frm.doc.name, employee: employee, diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.json b/erpnext/payroll/doctype/salary_structure/salary_structure.json similarity index 71% rename from erpnext/hr/doctype/salary_structure/salary_structure.json rename to erpnext/payroll/doctype/salary_structure/salary_structure.json index 58c4044093..5f94929f0b 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.json +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.json @@ -22,10 +22,9 @@ "leave_encashment_amount_per_day", "max_benefits", "earning_deduction", - "earning", "earnings", - "deduction", "deductions", + "conditions_and_formula_variable_and_example", "net_pay_detail", "column_break2", "total_earning", @@ -44,17 +43,23 @@ "label": "Company", "options": "Company", "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "letter_head", "fieldtype": "Link", "label": "Letter Head", - "options": "Letter Head" + "options": "Letter Head", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -67,7 +72,9 @@ "oldfieldname": "is_active", "oldfieldtype": "Select", "options": "\nYes\nNo", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "Monthly", @@ -75,7 +82,9 @@ "fieldname": "payroll_frequency", "fieldtype": "Select", "label": "Payroll Frequency", - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -86,46 +95,62 @@ "no_copy": 1, "options": "Yes\nNo", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "time_sheet_earning_detail", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "salary_slip_based_on_timesheet", "fieldtype": "Check", - "label": "Salary Slip Based on Timesheet" + "label": "Salary Slip Based on Timesheet", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_17", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Salary Component for timesheet based payroll.", "fieldname": "salary_component", "fieldtype": "Link", "label": "Salary Component", - "options": "Salary Component" + "options": "Salary Component", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "hour_rate", "fieldtype": "Currency", "label": "Hour Rate", - "options": "Company:company:default_currency" + "options": "Company:company:default_currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "leave_encashment_amount_per_day", "fieldtype": "Currency", "label": "Leave Encashment Amount Per Day", - "options": "Company:company:default_currency" + "options": "Company:company:default_currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "max_benefits", "fieldtype": "Currency", "label": "Max Benefits (Amount)", - "options": "Company:company:default_currency" + "options": "Company:company:default_currency", + "show_days": 1, + "show_seconds": 1 }, { "description": "Salary breakup based on Earning and Deduction.", @@ -133,15 +158,9 @@ "fieldtype": "Section Break", "oldfieldname": "earning_deduction", "oldfieldtype": "Section Break", - "precision": "2" - }, - { - "fieldname": "earning", - "fieldtype": "Section Break", - "label": "Earning", - "oldfieldname": "col_brk2", - "oldfieldtype": "Column Break", - "width": "50%" + "precision": "2", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "earnings", @@ -149,15 +168,9 @@ "label": "Earnings", "oldfieldname": "earning_details", "oldfieldtype": "Table", - "options": "Salary Detail" - }, - { - "fieldname": "deduction", - "fieldtype": "Section Break", - "label": "Deduction", - "oldfieldname": "col_brk3", - "oldfieldtype": "Column Break", - "width": "50%" + "options": "Salary Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "deductions", @@ -165,16 +178,22 @@ "label": "Deductions", "oldfieldname": "deduction_details", "oldfieldtype": "Table", - "options": "Salary Detail" + "options": "Salary Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_pay_detail", "fieldtype": "Section Break", - "options": "Simple" + "options": "Simple", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -185,7 +204,9 @@ "oldfieldname": "total_earning", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_deduction", @@ -195,7 +216,9 @@ "oldfieldname": "total_deduction", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_pay", @@ -203,28 +226,38 @@ "hidden": 1, "label": "Net Pay", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "account", "fieldtype": "Section Break", - "label": "Account" + "label": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", - "options": "Mode of Payment" + "options": "Mode of Payment", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_28", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_account", "fieldtype": "Link", "label": "Payment Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -233,16 +266,25 @@ "no_copy": 1, "options": "Salary Structure", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "conditions_and_formula_variable_and_example", + "fieldtype": "HTML", + "label": "Conditions and Formula variable and example", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2019-12-31 16:34:35.087658", + "modified": "2020-06-22 17:07:26.129355", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Structure", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py similarity index 100% rename from erpnext/hr/doctype/salary_structure/salary_structure.py rename to erpnext/payroll/doctype/salary_structure/salary_structure.py diff --git a/erpnext/hr/doctype/salary_structure/salary_structure_dashboard.py b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py similarity index 100% rename from erpnext/hr/doctype/salary_structure/salary_structure_dashboard.py rename to erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.js b/erpnext/payroll/doctype/salary_structure/test_salary_structure.js similarity index 100% rename from erpnext/hr/doctype/salary_structure/test_salary_structure.js rename to erpnext/payroll/doctype/salary_structure/test_salary_structure.js diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py similarity index 94% rename from erpnext/hr/doctype/salary_structure/test_salary_structure.py rename to erpnext/payroll/doctype/salary_structure/test_salary_structure.py index eb5311e3b8..e04fda8120 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py @@ -7,11 +7,11 @@ import unittest import erpnext from frappe.utils.make_random import get_random from frappe.utils import nowdate, add_days, add_years, getdate, add_months -from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip -from erpnext.hr.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\ +from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip +from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\ make_deduction_salary_component, make_employee_salary_slip, create_tax_slab from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period +from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period test_dependencies = ["Fiscal Year"] @@ -62,7 +62,7 @@ class TestSalaryStructure(unittest.TestCase): self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount) def test_amount_totals(self): - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"}) if not sal_slip: sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample") @@ -128,7 +128,7 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do salary_structure_doc.insert() if not dont_submit: salary_structure_doc.submit() - + else: salary_structure_doc = frappe.get_doc("Salary Structure", salary_structure) diff --git a/erpnext/hr/print_format/salary_slip_based_on_timesheet/__init__.py b/erpnext/payroll/doctype/salary_structure_assignment/__init__.py similarity index 100% rename from erpnext/hr/print_format/salary_slip_based_on_timesheet/__init__.py rename to erpnext/payroll/doctype/salary_structure_assignment/__init__.py diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js rename to erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json similarity index 98% rename from erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json rename to erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json index 0098aa8ec8..c84e034c72 100644 --- a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -124,9 +124,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-25 18:24:23.617088", + "modified": "2020-06-22 19:58:09.964692", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Structure Assignment", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.py rename to erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py diff --git a/erpnext/hr/doctype/salary_structure_assignment/test_salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/test_salary_structure_assignment.js rename to erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js diff --git a/erpnext/hr/doctype/salary_structure_assignment/test_salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/test_salary_structure_assignment.py rename to erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py diff --git a/erpnext/hr/print_format/salary_slip_standard/__init__.py b/erpnext/payroll/doctype/taxable_salary_slab/__init__.py similarity index 100% rename from erpnext/hr/print_format/salary_slip_standard/__init__.py rename to erpnext/payroll/doctype/taxable_salary_slab/__init__.py diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json new file mode 100644 index 0000000000..94eda4c043 --- /dev/null +++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json @@ -0,0 +1,66 @@ +{ + "actions": [], + "creation": "2018-04-13 17:42:13.516032", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "from_amount", + "to_amount", + "percent_deduction", + "condition", + "column_break_5", + "html_6" + ], + "fields": [ + { + "default": "0", + "fieldname": "from_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "From Amount", + "reqd": 1 + }, + { + "fieldname": "to_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "To Amount" + }, + { + "default": "0", + "fieldname": "percent_deduction", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Percent Deduction", + "reqd": 1 + }, + { + "fieldname": "condition", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Condition" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "html_6", + "fieldtype": "HTML", + "options": "

Condition Examples

\n
    \n
  1. Applying tax if employee born between 31-12-1937 and 01-01-1958 (Employees aged 60 to 80)
    \nCondition: date_of_birth>date(1937, 12, 31) and date_of_birth<date(1958, 01, 01)

  2. Applying tax by employee gender
    \nCondition: gender==\"Male\"

  3. \n
  4. Applying tax by Salary Component
    \nCondition: base > 10000
" + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-22 18:16:07.596493", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Taxable Salary Slab", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.py b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py similarity index 71% rename from erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.py rename to erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py index 23e5ffbe62..49c52557db 100644 --- a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.py +++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class TaxableSalarySlab(Document): diff --git a/erpnext/payroll/module_onboarding/payroll/payroll.json b/erpnext/payroll/module_onboarding/payroll/payroll.json new file mode 100644 index 0000000000..7ed786faee --- /dev/null +++ b/erpnext/payroll/module_onboarding/payroll/payroll.json @@ -0,0 +1,51 @@ +{ + "allow_roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ], + "creation": "2020-06-01 12:10:52.560472", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources/payroll-entry", + "idx": 0, + "is_complete": 0, + "modified": "2020-06-29 17:00:25.113341", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll", + "owner": "Administrator", + "steps": [ + { + "step": "Create Employee" + }, + { + "step": "Create Salary Component" + }, + { + "step": "Create Payroll Period" + }, + { + "step": "Create Income Tax Slab" + }, + { + "step": "Create Salary Structure" + }, + { + "step": "Assign Salary Structure" + }, + { + "step": "Create Salary Slip" + }, + { + "step": "Payroll Settings" + } + ], + "subtitle": "Salary, Compensations and more.", + "success_message": "The Payroll is all set up!", + "title": "Let's Setup the Payroll Module. ", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/notification/as b/erpnext/payroll/notification/as new file mode 100644 index 0000000000..7a39557261 --- /dev/null +++ b/erpnext/payroll/notification/as @@ -0,0 +1 @@ +update from `tabNotification` set module='Payroll' where name = "Retention Bonus" \ No newline at end of file diff --git a/erpnext/hr/report/bank_remittance/__init__.py b/erpnext/payroll/notification/retention_bonus/__init__.py similarity index 100% rename from erpnext/hr/report/bank_remittance/__init__.py rename to erpnext/payroll/notification/retention_bonus/__init__.py diff --git a/erpnext/hr/notification/retention_bonus/retention_bonus.json b/erpnext/payroll/notification/retention_bonus/retention_bonus.json similarity index 86% rename from erpnext/hr/notification/retention_bonus/retention_bonus.json rename to erpnext/payroll/notification/retention_bonus/retention_bonus.json index cbc8e2d078..50db0338c4 100644 --- a/erpnext/hr/notification/retention_bonus/retention_bonus.json +++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.json @@ -13,10 +13,10 @@ "is_standard": 1, "message": "

{{ _(\"Hello\") }},

\n\n

{{ _(\"Retention Bonus for\") }} {{ doc.employee_name }} {{ _(\"due on\") }} {{ doc.bonus_payment_date }}

", "modified": "2018-05-15 19:00:24.294418", - "modified_by": "ranjith@earthianslive.com", - "module": "HR", + "modified_by": "Administrator", + "module": "Payroll", "name": "Retention Bonus", - "owner": "ranjith@earthianslive.com", + "owner": "Administrator", "recipients": [ { "email_by_role": "HR Manager" diff --git a/erpnext/hr/notification/retention_bonus/retention_bonus.md b/erpnext/payroll/notification/retention_bonus/retention_bonus.md similarity index 100% rename from erpnext/hr/notification/retention_bonus/retention_bonus.md rename to erpnext/payroll/notification/retention_bonus/retention_bonus.md diff --git a/erpnext/hr/notification/retention_bonus/retention_bonus.py b/erpnext/payroll/notification/retention_bonus/retention_bonus.py similarity index 100% rename from erpnext/hr/notification/retention_bonus/retention_bonus.py rename to erpnext/payroll/notification/retention_bonus/retention_bonus.py diff --git a/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json b/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json new file mode 100644 index 0000000000..8a07b10276 --- /dev/null +++ b/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:58:43.927590", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:58:43.927590", + "modified_by": "Administrator", + "name": "Assign Salary Structure", + "owner": "Administrator", + "reference_document": "Salary Structure Assignment", + "show_full_form": 1, + "title": "Assign Salary Structure", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_employee/create_employee.json b/erpnext/payroll/onboarding_step/create_employee/create_employee.json new file mode 100644 index 0000000000..5839ae6ca4 --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_employee/create_employee.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:43:25.561152", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:26:28.629074", + "modified_by": "Administrator", + "name": "Create Employee", + "owner": "Administrator", + "reference_document": "Employee", + "show_full_form": 0, + "title": "Create Employee", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json b/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json new file mode 100644 index 0000000000..faada7e411 --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:54:54.823796", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:54:54.823796", + "modified_by": "Administrator", + "name": "Create Income Tax Slab", + "owner": "Administrator", + "reference_document": "Income Tax Slab", + "show_full_form": 1, + "title": "Create Income Tax Slab", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json new file mode 100644 index 0000000000..b1a7cc2734 --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:53:54.553947", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-29 11:53:54.553947", + "modified_by": "Administrator", + "name": "Create Payroll Period", + "owner": "Administrator", + "reference_document": "Payroll Period", + "show_full_form": 0, + "title": "Create Payroll Period", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json b/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json new file mode 100644 index 0000000000..002d819618 --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:57:04.002073", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:57:04.002073", + "modified_by": "Administrator", + "name": "Create Salary Component", + "owner": "Administrator", + "reference_document": "Salary Component", + "show_full_form": 1, + "title": "Create Salary Component", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json b/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json new file mode 100644 index 0000000000..2aa31f485f --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:59:29.972393", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:59:29.972393", + "modified_by": "Administrator", + "name": "Create Salary Slip", + "owner": "Administrator", + "reference_document": "Salary Slip", + "show_full_form": 1, + "title": "Create Salary Slip", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json b/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json new file mode 100644 index 0000000000..11d8327259 --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:57:54.527808", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:57:54.527808", + "modified_by": "Administrator", + "name": "Create Salary Structure", + "owner": "Administrator", + "reference_document": "Salary Structure", + "show_full_form": 1, + "title": "Create Salary Structure", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json new file mode 100644 index 0000000000..a7cf7bf988 --- /dev/null +++ b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Update Settings", + "creation": "2020-06-04 16:34:29.664917", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-06-29 16:34:29.664917", + "modified_by": "Administrator", + "name": "Payroll Settings", + "owner": "Administrator", + "reference_document": "Payroll Settings", + "show_full_form": 0, + "title": "Payroll Settings", + "validate_action": 0 +} \ No newline at end of file diff --git a/erpnext/hr/report/salary_register/__init__.py b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/__init__.py similarity index 100% rename from erpnext/hr/report/salary_register/__init__.py rename to erpnext/payroll/print_format/salary_slip_based_on_timesheet/__init__.py diff --git a/erpnext/hr/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json similarity index 100% rename from erpnext/hr/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json rename to erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json diff --git a/erpnext/stock/report/ordered_items_to_be_delivered/__init__.py b/erpnext/payroll/print_format/salary_slip_standard/__init__.py similarity index 100% rename from erpnext/stock/report/ordered_items_to_be_delivered/__init__.py rename to erpnext/payroll/print_format/salary_slip_standard/__init__.py diff --git a/erpnext/hr/print_format/salary_slip_standard/salary_slip_standard.json b/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json similarity index 100% rename from erpnext/hr/print_format/salary_slip_standard/salary_slip_standard.json rename to erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json diff --git a/erpnext/support/doctype/service_level/__init__.py b/erpnext/payroll/report/__init__.py similarity index 100% rename from erpnext/support/doctype/service_level/__init__.py rename to erpnext/payroll/report/__init__.py diff --git a/erpnext/payroll/report/bank_remittance/__init__.py b/erpnext/payroll/report/bank_remittance/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/hr/report/bank_remittance/bank_remittance.js b/erpnext/payroll/report/bank_remittance/bank_remittance.js similarity index 68% rename from erpnext/hr/report/bank_remittance/bank_remittance.js rename to erpnext/payroll/report/bank_remittance/bank_remittance.js index 1e10f24301..6482ed3451 100644 --- a/erpnext/hr/report/bank_remittance/bank_remittance.js +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.js @@ -5,12 +5,12 @@ frappe.query_reports["Bank Remittance"] = { "filters": [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname:"company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 }, { fieldname:"from_date", diff --git a/erpnext/hr/report/bank_remittance/bank_remittance.json b/erpnext/payroll/report/bank_remittance/bank_remittance.json similarity index 87% rename from erpnext/hr/report/bank_remittance/bank_remittance.json rename to erpnext/payroll/report/bank_remittance/bank_remittance.json index b8aa4e98d2..2a697b2589 100644 --- a/erpnext/hr/report/bank_remittance/bank_remittance.json +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.json @@ -7,9 +7,9 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2019-04-26 16:57:52.558895", + "modified": "2020-05-28 00:08:08.097494", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Bank Remittance", "owner": "Administrator", "prepared_report": 0, diff --git a/erpnext/hr/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py similarity index 92% rename from erpnext/hr/report/bank_remittance/bank_remittance.py rename to erpnext/payroll/report/bank_remittance/bank_remittance.py index b2d2c53024..4b052bf5c4 100644 --- a/erpnext/hr/report/bank_remittance/bank_remittance.py +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py @@ -125,7 +125,10 @@ def get_salary_slips(payroll_entries): # appending company debit accounts for slip in salary_slips: - slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]['company_account'] + if slip.payroll_entry: + slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]['company_account'] + else: + slip["debit_acc_no"] = None return salary_slips @@ -149,6 +152,9 @@ def set_company_account(payment_accounts, payroll_entries): company_accounts_map[acc.account] = acc for entry in payroll_entries: - entry["company_account"] = company_accounts_map[entry.payment_account]['bank_account_no'] + company_account = '' + if entry.payment_account in company_accounts_map: + company_account = company_accounts_map[entry.payment_account]['bank_account_no'] + entry["company_account"] = company_account return payroll_entries diff --git a/erpnext/payroll/report/income_tax_deductions/__init__.py b/erpnext/payroll/report/income_tax_deductions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js new file mode 100644 index 0000000000..4bbb7f6a1b --- /dev/null +++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Income Tax Deductions"] = erpnext.salary_slip_deductions_report_filters; +}); \ No newline at end of file diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json new file mode 100644 index 0000000000..cf80398f98 --- /dev/null +++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-05-30 00:07:56.744372", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-30 00:07:56.744372", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Income Tax Deductions", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Income Tax Deductions", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + }, + { + "role": "Employee" + } + ] +} \ No newline at end of file diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py new file mode 100644 index 0000000000..8a79416edb --- /dev/null +++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py @@ -0,0 +1,133 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ + +def execute(filters=None): + data = get_data(filters) + columns = get_columns(filters) if len(data) else [] + + return columns, data + +def get_columns(filters): + columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 200 + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160 + }] + + if erpnext.get_region() == "India": + columns.append({ + "label": _("PAN Number"), + "fieldname": "pan_number", + "fieldtype": "Data", + "width": 140 + }) + + columns += [{ + "label": _("Income Tax Component"), + "fieldname": "it_comp", + "fieldtype": "Data", + "width": 170 + }, + { + "label": _("Income Tax Amount"), + "fieldname": "it_amount", + "fieldtype": "Currency", + "options": "currency", + "width": 140 + }, + { + "label": _("Gross Pay"), + "fieldname": "gross_pay", + "fieldtype": "Currency", + "options": "currency", + "width": 140 + }, + { + "label": _("Posting Date"), + "fieldname": "posting_date", + "fieldtype": "Date", + "width": 140 + } + ] + + return columns + +def get_conditions(filters): + conditions = [""] + + if filters.get("department"): + conditions.append("sal.department = '%s' " % (filters["department"]) ) + + if filters.get("branch"): + conditions.append("sal.branch = '%s' " % (filters["branch"]) ) + + if filters.get("company"): + conditions.append("sal.company = '%s' " % (filters["company"]) ) + + if filters.get("month"): + conditions.append("month(sal.start_date) = '%s' " % (filters["month"])) + + if filters.get("year"): + conditions.append("year(start_date) = '%s' " % (filters["year"])) + + return " and ".join(conditions) + + +def get_data(filters): + + data = [] + + if erpnext.get_region() == "India": + employee_pan_dict = frappe._dict(frappe.db.sql(""" select employee, pan_number from `tabEmployee`""")) + + component_types = frappe.db.sql(""" select name from `tabSalary Component` + where is_income_tax_component = 1 """) + + component_types = [comp_type[0] for comp_type in component_types] + + if not len(component_types): + return [] + + conditions = get_conditions(filters) + + entry = frappe.db.sql(""" select sal.employee, sal.employee_name, sal.posting_date, ded.salary_component, ded.amount,sal.gross_pay + from `tabSalary Slip` sal, `tabSalary Detail` ded + where sal.name = ded.parent + and ded.parentfield = 'deductions' + and ded.parenttype = 'Salary Slip' + and sal.docstatus = 1 %s + and ded.salary_component in (%s) + """ % (conditions , ", ".join(['%s']*len(component_types))), tuple(component_types), as_dict=1) + + for d in entry: + + employee = { + "employee": d.employee, + "employee_name": d.employee_name, + "it_comp": d.salary_component, + "posting_date": d.posting_date, + # "pan_number": employee_pan_dict.get(d.employee), + "it_amount": d.amount, + "gross_pay": d.gross_pay + } + + if erpnext.get_region() == "India": + employee["pan_number"] = employee_pan_dict.get(d.employee) + + data.append(employee) + + return data diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js new file mode 100644 index 0000000000..166d982c9c --- /dev/null +++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Salary Payments Based On Payment Mode"] = erpnext.salary_slip_deductions_report_filters; +}); \ No newline at end of file diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json new file mode 100644 index 0000000000..c04cc32b9b --- /dev/null +++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-06-16 18:43:43.107246", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 18:43:43.107246", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Payments Based On Payment Mode", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Salary Payments Based On Payment Mode", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + }, + { + "role": "Employee" + } + ] +} \ No newline at end of file diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py new file mode 100644 index 0000000000..a0dab63654 --- /dev/null +++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py @@ -0,0 +1,179 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ +from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions + +def execute(filters=None): + mode_of_payments = get_payment_modes() + + if not len(mode_of_payments): + return [], [] + + columns = get_columns(filters, mode_of_payments) + data, total_rows, report_summary = get_data(filters, mode_of_payments) + chart = get_chart(mode_of_payments, total_rows) + + return columns, data, None, chart, report_summary + +def get_columns(filters, mode_of_payments): + columns = [{ + "label": _("Branch"), + "options": "Branch", + "fieldname": "branch", + "fieldtype": "Link", + "width": 200 + }] + + for mode in mode_of_payments: + columns.append({ + "label": _(mode), + "fieldname": mode, + "fieldtype": "Currency", + "width": 160 + }) + + columns.append({ + "label": _("Total"), + "fieldname": "total", + "fieldtype": "Currency", + "width": 140 + }) + + return columns + +def get_payment_modes(): + mode_of_payments = frappe.db.sql_list(""" + select distinct mode_of_payment from `tabSalary Slip` where docstatus = 1 + """) + return mode_of_payments + +def prepare_data(entry): + branch_wise_entries = {} + gross_pay = 0 + + for d in entry: + gross_pay += d.gross_pay + if branch_wise_entries.get(d.branch): + branch_wise_entries[d.branch][d.mode_of_payment] = d.net_pay + else: + branch_wise_entries.setdefault(d.branch, {}).setdefault(d.mode_of_payment, d.net_pay) + + return branch_wise_entries, gross_pay + +def get_data(filters, mode_of_payments): + data = [] + + conditions = get_conditions(filters) + + entry = frappe.db.sql(""" + select branch, mode_of_payment, sum(net_pay) as net_pay, sum(gross_pay) as gross_pay + from `tabSalary Slip` sal + where docstatus = 1 %s + group by branch, mode_of_payment + """ % (conditions), as_dict=1) + + branch_wise_entries, gross_pay = prepare_data(entry) + + branches = frappe.db.sql_list(""" + select distinct branch from `tabSalary Slip` sal + where docstatus = 1 %s + """ % (conditions)) + + total_row = {"total": 0, "branch": "Total"} + + for branch in branches: + total = 0 + row = { + "branch": branch + } + for mode in mode_of_payments: + if branch_wise_entries.get(branch).get(mode): + row[mode] = branch_wise_entries.get(branch).get(mode) + total += branch_wise_entries.get(branch).get(mode) + + row["total"] = total + data.append(row) + + total_row = get_total_based_on_mode_of_payment(data, mode_of_payments) + total_deductions = gross_pay - total_row.get("total") + + report_summary = [] + + if data: + data.append(total_row) + data.append({}) + data.append({ + "branch": "Total Gross Pay", + mode_of_payments[0]:gross_pay + }) + data.append({ + "branch": "Total Deductions", + mode_of_payments[0]:total_deductions + }) + data.append({ + "branch": "Total Net Pay", + mode_of_payments[0]:total_row.get("total") + }) + + currency = erpnext.get_company_currency(filters.company) + report_summary = get_report_summary(gross_pay, total_deductions, total_row.get("total"), currency) + + return data, total_row, report_summary + +def get_total_based_on_mode_of_payment(data, mode_of_payments): + + total = 0 + total_row = {"branch": "Total"} + for mode in mode_of_payments: + sum_of_payment = sum([detail[mode] for detail in data if mode in detail.keys()]) + total_row[mode] = sum_of_payment + total += sum_of_payment + + total_row["total"] = total + return total_row + +def get_report_summary(gross_pay, total_deductions, net_pay, currency): + return [ + { + "value": gross_pay, + "label": "Total Gross Pay", + "indicator": "Green", + "datatype": "Currency", + "currency": currency + }, + { + "value": total_deductions, + "label": "Total Deduction", + "datatype": "Currency", + "indicator": "Red", + "currency": currency + }, + { + "value": net_pay, + "label": "Total Net Pay", + "datatype": "Currency", + "indicator": "Blue", + "currency": currency + } + ] + +def get_chart(mode_of_payments, data): + if data: + values = [] + labels = [] + + for mode in mode_of_payments: + values.append(data[mode]) + labels.append([mode]) + + chart = { + "data": { + "labels": labels, + "datasets": [{'name': 'Mode Of Payments', "values": values}] + } + } + chart['type'] = "bar" + return chart diff --git a/erpnext/payroll/report/salary_payments_via_ecs/__init__.py b/erpnext/payroll/report/salary_payments_via_ecs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js new file mode 100644 index 0000000000..e49fc112ff --- /dev/null +++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js @@ -0,0 +1,16 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + + let ecs_checklist_filter = erpnext.salary_slip_deductions_report_filters + ecs_checklist_filter['filters'].push({ + fieldname: "type", + label: __("Type"), + fieldtype: "Select", + options:["", "Bank", "Cash", "Cheque"] + }) + + frappe.query_reports["Salary Payments via ECS"] = ecs_checklist_filter +}); diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json new file mode 100644 index 0000000000..dd0ac7c4ef --- /dev/null +++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "creation": "2020-06-16 18:35:30.508143", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 18:38:23.680185", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Payments via ECS", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Salary Payments via ECS", + "report_type": "Script Report", + "roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ] +} \ No newline at end of file diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py new file mode 100644 index 0000000000..d09745c37b --- /dev/null +++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py @@ -0,0 +1,149 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ + +def execute(filters=None): + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_columns(filters): + columns = [ + { + "label": _("Branch"), + "options": "Branch", + "fieldname": "branch", + "fieldtype": "Link", + "width": 200 + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160 + }, + { + "label": _("Employee"), + "options":"Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Gross Pay"), + "fieldname": "gross_pay", + "fieldtype": "Currency", + "options": "currency", + "width": 140 + }, + { + "label": _("Bank"), + "fieldname": "bank", + "fieldtype": "Data", + "width": 140 + }, + { + "label": _("Account No"), + "fieldname": "account_no", + "fieldtype": "Data", + "width": 140 + }, + ] + if erpnext.get_region() == "India": + columns += [ + { + "label": _("IFSC"), + "fieldname": "ifsc", + "fieldtype": "Data", + "width": 140 + }, + { + "label": _("MICR"), + "fieldname": "micr", + "fieldtype": "Data", + "width": 140 + } + ] + + return columns + +def get_conditions(filters): + conditions = [""] + + if filters.get("department"): + conditions.append("department = '%s' " % (filters["department"]) ) + + if filters.get("branch"): + conditions.append("branch = '%s' " % (filters["branch"]) ) + + if filters.get("company"): + conditions.append("company = '%s' " % (filters["company"]) ) + + if filters.get("month"): + conditions.append("month(start_date) = '%s' " % (filters["month"])) + + if filters.get("year"): + conditions.append("year(start_date) = '%s' " % (filters["year"])) + + return " and ".join(conditions) + +def get_data(filters): + + data = [] + + fields = ["employee", "branch", "bank_name", "bank_ac_no", "salary_mode"] + if erpnext.get_region() == "India": + fields += ["ifsc_code", "micr_code"] + + + employee_details = frappe.get_list("Employee", fields = fields) + employee_data_dict = {} + + for d in employee_details: + employee_data_dict.setdefault( + d.employee,{ + "bank_ac_no" : d.bank_ac_no, + "ifsc_code" : d.ifsc_code or None, + "micr_code" : d.micr_code or None, + "branch" : d.branch, + "salary_mode" : d.salary_mode, + "bank_name": d.bank_name + } + ) + + conditions = get_conditions(filters) + + entry = frappe.db.sql(""" select employee, employee_name, gross_pay + from `tabSalary Slip` + where docstatus = 1 %s """ + %(conditions), as_dict =1) + + for d in entry: + + employee = { + "branch" : employee_data_dict.get(d.employee).get("branch"), + "employee_name" : d.employee_name, + "employee" : d.employee, + "gross_pay" : d.gross_pay, + } + + if employee_data_dict.get(d.employee).get("salary_mode") == "Bank": + employee["bank"] = employee_data_dict.get(d.employee).get("bank_name") + employee["account_no"] = employee_data_dict.get(d.employee).get("bank_ac_no") + if erpnext.get_region() == "India": + employee["ifsc"] = employee_data_dict.get(d.employee).get("ifsc_code") + employee["micr"] = employee_data_dict.get(d.employee).get("micr_code") + else: + employee["account_no"] = employee_data_dict.get(d.employee).get("salary_mode") + + if filters.get("type") and employee_data_dict.get(d.employee).get("salary_mode") == filters.get("type"): + data.append(employee) + elif not filters.get("type"): + data.append(employee) + + return data diff --git a/erpnext/payroll/report/salary_register/__init__.py b/erpnext/payroll/report/salary_register/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/hr/report/salary_register/salary_register.html b/erpnext/payroll/report/salary_register/salary_register.html similarity index 100% rename from erpnext/hr/report/salary_register/salary_register.html rename to erpnext/payroll/report/salary_register/salary_register.html diff --git a/erpnext/hr/report/salary_register/salary_register.js b/erpnext/payroll/report/salary_register/salary_register.js similarity index 100% rename from erpnext/hr/report/salary_register/salary_register.js rename to erpnext/payroll/report/salary_register/salary_register.js diff --git a/erpnext/payroll/report/salary_register/salary_register.json b/erpnext/payroll/report/salary_register/salary_register.json new file mode 100644 index 0000000000..5a70c32593 --- /dev/null +++ b/erpnext/payroll/report/salary_register/salary_register.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 1, + "creation": "2017-01-10 17:36:58.153863", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "modified": "2020-05-28 00:07:18.576661", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Register", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Salary Register", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/hr/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py similarity index 96% rename from erpnext/hr/report/salary_register/salary_register.py rename to erpnext/payroll/report/salary_register/salary_register.py index ea7fc06cff..87010855fd 100644 --- a/erpnext/hr/report/salary_register/salary_register.py +++ b/erpnext/payroll/report/salary_register/salary_register.py @@ -55,8 +55,8 @@ def get_columns(salary_slips): columns = [ _("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Date of Joining") + "::80", _("Branch") + ":Link/Branch:-1", _("Department") + ":Link/Department:-1", - _("Designation") + ":Link/Designation:-1", _("Company") + ":Link/Company:120", _("Start Date") + "::80", - _("End Date") + "::80", _("Leave Without Pay") + ":Float:-1", _("Payment Days") + ":Float:120" + _("Designation") + ":Link/Designation:120", _("Company") + ":Link/Company:120", _("Start Date") + "::80", + _("End Date") + "::80", _("Leave Without Pay") + ":Float:50", _("Payment Days") + ":Float:120" ] salary_components = {_("Earning"): [], _("Deduction"): []} diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 0993e69e04..6b6b8c579b 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -1,4 +1,5 @@ import frappe +from frappe.utils import cint from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager def get_field_filter_data(): @@ -243,6 +244,8 @@ def get_next_attribute_and_values(item_code, selected_attributes): else: product_info = None + product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) + return { 'next_attribute': next_attribute, 'valid_options_for_attributes': valid_options_for_attributes, diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index d91fe5304a..e24cf3081c 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -68,7 +68,7 @@ "type": "Report" }, { - "label": "Project Dashboard", + "label": "Dashboard", "link_to": "Project", "type": "Dashboard" } diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 5862963496..3570a0f2be 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -18,7 +18,7 @@ frappe.ui.form.on("Project", { }; }, onload: function (frm) { - var so = frm.get_docfield("Project", "sales_order"); + var so = frappe.meta.get_docfield("Project", "sales_order"); so.get_route_options_for_new_doc = function (field) { if (frm.is_new()) return; return { @@ -135,4 +135,4 @@ function open_form(frm, doctype, child_doctype, parentfield) { frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); -} +} \ No newline at end of file diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py index bd3369447b..47a28fd111 100644 --- a/erpnext/projects/doctype/task/test_task.py +++ b/erpnext/projects/doctype/task/test_task.py @@ -64,7 +64,7 @@ class TestTask(unittest.TestCase): def assign(): from frappe.desk.form import assign_to assign_to.add({ - "assign_to": "test@example.com", + "assign_to": ["test@example.com"], "doctype": task.doctype, "name": task.name, "description": "Close this task" diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index 32f0428fcd..03b67b1023 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -11,9 +11,9 @@ from frappe.utils import now_datetime, nowdate, add_days, add_months from erpnext.projects.doctype.timesheet.timesheet import OverlapError from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice -from erpnext.hr.doctype.salary_structure.test_salary_structure \ +from erpnext.payroll.doctype.salary_structure.test_salary_structure \ import make_salary_structure, create_salary_structure_assignment - +from erpnext.hr.doctype.employee.test_employee import make_employee class TestTimesheet(unittest.TestCase): def setUp(self): @@ -25,8 +25,10 @@ class TestTimesheet(unittest.TestCase): def test_timesheet_billing_amount(self): - make_salary_structure_for_timesheet("_T-Employee-00001") - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1) + emp = make_employee("test_employee_6@salary.com") + + make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate=True, billable=1) self.assertEqual(timesheet.total_hours, 2) self.assertEqual(timesheet.total_billable_hours, 2) @@ -35,8 +37,10 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(timesheet.total_billable_amount, 100) def test_timesheet_billing_amount_not_billable(self): - make_salary_structure_for_timesheet("_T-Employee-00001") - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=0) + emp = make_employee("test_employee_6@salary.com") + + make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate=True, billable=0) self.assertEqual(timesheet.total_hours, 2) self.assertEqual(timesheet.total_billable_hours, 0) @@ -45,8 +49,10 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(timesheet.total_billable_amount, 0) def test_salary_slip_from_timesheet(self): - salary_structure = make_salary_structure_for_timesheet("_T-Employee-00001") - timesheet = make_timesheet("_T-Employee-00001", simulate = True, billable=1) + emp = make_employee("test_employee_6@salary.com") + + salary_structure = make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate = True, billable=1) salary_slip = make_salary_slip(timesheet.name) salary_slip.submit() @@ -65,7 +71,9 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(timesheet.status, 'Submitted') def test_sales_invoice_from_timesheet(self): - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1) + emp = make_employee("test_employee_6@salary.com") + + timesheet = make_timesheet(emp, simulate=True, billable=1) sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer') sales_invoice.due_date = nowdate() sales_invoice.submit() @@ -80,7 +88,9 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(item.rate, 50.00) def test_timesheet_billing_based_on_project(self): - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1, project = '_Test Project', company='_Test Company') + emp = make_employee("test_employee_6@salary.com") + + timesheet = make_timesheet(emp, simulate=True, billable=1, project = '_Test Project', company='_Test Company') sales_invoice = create_sales_invoice(do_not_save=True) sales_invoice.project = '_Test Project' sales_invoice.submit() @@ -90,6 +100,8 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(ts.time_logs[0].sales_invoice, sales_invoice.name) def test_timesheet_time_overlap(self): + emp = make_employee("test_employee_6@salary.com") + settings = frappe.get_single('Projects Settings') initial_setting = settings.ignore_employee_time_overlap settings.ignore_employee_time_overlap = 0 @@ -97,7 +109,7 @@ class TestTimesheet(unittest.TestCase): update_activity_type("_Test Activity Type") timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "_T-Employee-00001" + timesheet.employee = emp timesheet.append( 'time_logs', { @@ -129,12 +141,14 @@ class TestTimesheet(unittest.TestCase): settings.save() def test_timesheet_std_working_hours(self): + emp = make_employee("test_employee_6@salary.com") + company = frappe.get_doc('Company', "_Test Company") company.standard_working_hours = 8 company.save() timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "_T-Employee-00001" + timesheet.employee = emp timesheet.company = '_Test Company' timesheet.append( 'time_logs', @@ -156,7 +170,7 @@ class TestTimesheet(unittest.TestCase): company.save() timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "_T-Employee-00001" + timesheet.employee = emp timesheet.company = '_Test Company' timesheet.append( 'time_logs', diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 3eea390ff3..defc18bf4e 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -258,7 +258,12 @@ var calculate_end_time = function(frm, cdt, cdn) { var update_billing_hours = function(frm, cdt, cdn){ var child = locals[cdt][cdn]; - if(!child.billable) frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0); + if(!child.billable) { + frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0); + } else { + // bill all hours by default + frappe.model.set_value(cdt, cdn, "billing_hours", child.hours); + } }; var update_time_rates = function(frm, cdt, cdn){ diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 9c56189476..a4cc68b3e2 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -73,6 +73,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ me.frm.set_query('contact_person', erpnext.queries.contact_query); me.frm.set_query('supplier_address', erpnext.queries.address_query); + me.frm.set_query('billing_address', erpnext.queries.company_address_query); + if(this.frm.fields_dict.supplier) { this.frm.set_query("supplier", function() { return{ query: "erpnext.controllers.queries.supplier_query" }}); @@ -283,6 +285,11 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ "shipping_address_display", true); }, + billing_address: function() { + erpnext.utils.get_address_display(this.frm, "billing_address", + "billing_address_display", true); + }, + tc_name: function() { this.get_terms(); }, diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js index 2ce49e766b..87b21b78de 100644 --- a/erpnext/public/js/controllers/stock_controller.js +++ b/erpnext/public/js/controllers/stock_controller.js @@ -55,8 +55,9 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ frappe.route_options = { voucher_no: me.frm.doc.name, from_date: me.frm.doc.posting_date, - to_date: me.frm.doc.posting_date, - company: me.frm.doc.company + to_date: moment(me.frm.doc.modified).format('YYYY-MM-DD'), + company: me.frm.doc.company, + show_cancelled_entries: me.frm.doc.docstatus === 2 }; frappe.set_route("query-report", "Stock Ledger"); }, __("View")); @@ -71,9 +72,10 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ frappe.route_options = { voucher_no: me.frm.doc.name, from_date: me.frm.doc.posting_date, - to_date: me.frm.doc.posting_date, + to_date: moment(me.frm.doc.modified).format('YYYY-MM-DD'), company: me.frm.doc.company, - group_by: "Group by Voucher (Consolidated)" + group_by: "Group by Voucher (Consolidated)", + show_cancelled_entries: me.frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __("View")); diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index dbe48ec654..b72ceb2113 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -335,6 +335,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ var tax_rate = this._get_tax_rate(tax, item_tax_map); var current_tax_amount = 0.0; + // To set row_id by default as previous row. + if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) { + if (tax.idx === 1) { + frappe.throw( + __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")); + } + if (!tax.row_id) { + tax.row_id = tax.idx - 1; + } + } if(tax.charge_type == "Actual") { // distribute the tax amount proportionally to each item row var actual = flt(tax.tax_amount, precision("tax_amount", tax)); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 637d3b3267..ca897dd4b1 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -552,7 +552,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if (show_batch_dialog) return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) .then((r) => { - if(r.message.has_batch_no || r.message.has_serial_no) { + if (r.message && + (r.message.has_batch_no || r.message.has_serial_no)) { frappe.flags.hide_serial_batch_dialog = false; } }); @@ -917,7 +918,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ shipping_rule: function() { var me = this; - if(this.frm.doc.shipping_rule && this.frm.doc.shipping_address) { + if(this.frm.doc.shipping_rule) { return this.frm.call({ doc: this.frm.doc, method: "apply_shipping_rule", @@ -1711,7 +1712,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, set_gross_profit: function(item) { - if (this.frm.doc.doctype == "Sales Order" && item.valuation_rate) { + if (["Sales Order", "Quotation"].includes(this.frm.doc.doctype) && item.valuation_rate) { var rate = flt(item.rate) * flt(this.frm.doc.conversion_rate || 1); item.gross_profit = flt(((rate - item.valuation_rate) * item.stock_qty), precision("amount", item)); } diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 296c6280d8..d89d4712e6 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -47,6 +47,16 @@ erpnext.financial_statements = { // dropdown for links to other financial statements erpnext.financial_statements.filters = get_filters() + let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + period_start_date: fy.year_start_date, + period_end_date: fy.year_end_date + }); + }); + report.page.add_inner_button(__("Balance Sheet"), function() { var filters = report.get_values(); frappe.set_route('query-report', 'Balance Sheet', {company: filters.company}); @@ -62,7 +72,7 @@ erpnext.financial_statements = { } }; -function get_filters(){ +function get_filters() { let filters = [ { "fieldname":"company", @@ -99,7 +109,6 @@ function get_filters(){ "fieldname":"period_start_date", "label": __("Start Date"), "fieldtype": "Date", - "default": frappe.datetime.nowdate(), "hidden": 1, "reqd": 1 }, @@ -107,7 +116,6 @@ function get_filters(){ "fieldname":"period_end_date", "label": __("End Date"), "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), "hidden": 1, "reqd": 1 }, @@ -162,15 +170,6 @@ function get_filters(){ } ] - erpnext.dimension_filters.forEach((dimension) => { - filters.push({ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); - return filters; } diff --git a/erpnext/public/js/salary_slip_deductions_report_filters.js b/erpnext/public/js/salary_slip_deductions_report_filters.js new file mode 100644 index 0000000000..2b30e65075 --- /dev/null +++ b/erpnext/public/js/salary_slip_deductions_report_filters.js @@ -0,0 +1,66 @@ +frappe.provide("erpnext.salary_slip_deductions_report_filters"); + +erpnext.salary_slip_deductions_report_filters = { + "filters": [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd:1, + default: frappe.defaults.get_user_default("Company"), + }, + { + fieldname: "month", + label: __("Month"), + fieldtype: "Select", + reqd: 1 , + options: [ + { "value": 1, "label": __("Jan") }, + { "value": 2, "label": __("Feb") }, + { "value": 3, "label": __("Mar") }, + { "value": 4, "label": __("Apr") }, + { "value": 5, "label": __("May") }, + { "value": 6, "label": __("June") }, + { "value": 7, "label": __("July") }, + { "value": 8, "label": __("Aug") }, + { "value": 9, "label": __("Sep") }, + { "value": 10, "label": __("Oct") }, + { "value": 11, "label": __("Nov") }, + { "value": 12, "label": __("Dec") }, + ], + default: frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1 + }, + { + fieldname:"year", + label: __("Year"), + fieldtype: "Select", + reqd: 1 + }, + { + fieldname: "department", + label: __("Department"), + fieldtype: "Link", + options: "Department", + }, + { + fieldname: "branch", + label: __("Barnch"), + fieldtype: "Link", + options: "Branch", + } + ], + + "onload": function() { + return frappe.call({ + method: "erpnext.regional.report.provident_fund_deductions.provident_fund_deductions.get_years", + callback: function(r) { + var year_filter = frappe.query_report.get_filter('year'); + year_filter.df.options = r.message; + year_filter.df.default = r.message.split("\n")[0]; + year_filter.refresh(); + year_filter.set_input(year_filter.df.default); + } + }); + } +} \ No newline at end of file diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 58969f2a9f..bcab0d84c6 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -191,6 +191,23 @@ $.extend(erpnext.utils, { }) }, + add_dimensions: function(report_name, index) { + let filters = frappe.query_reports[report_name].filters; + + erpnext.dimension_filters.forEach((dimension) => { + let found = filters.some(el => el.fieldname === dimension['fieldname']); + + if (!found) { + filters.splice(index, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + } + }); + }, + make_subscription: function(doctype, docname) { frappe.call({ method: "frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat", @@ -470,7 +487,14 @@ erpnext.utils.update_child_items = function(opts) { fieldtype: 'Date', fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date", in_list_view: 1, - label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") + label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date"), + reqd: 1 + }) + fields.splice(3, 0, { + fieldtype: 'Float', + fieldname: "conversion_factor", + in_list_view: 1, + label: __("Conversion Factor") }) } @@ -519,6 +543,7 @@ erpnext.utils.update_child_items = function(opts) { "item_code": d.item_code, "delivery_date": d.delivery_date, "schedule_date": d.schedule_date, + "conversion_factor": d.conversion_factor, "qty": d.qty, "rate": d.rate, }); diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss index 735b417da1..617e916724 100644 --- a/erpnext/public/scss/website.scss +++ b/erpnext/public/scss/website.scss @@ -81,4 +81,10 @@ .place-order-container { text-align: right; +} + +.kb-card { + .card-body > .card-title { + line-height: 1.3; + } } \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js index ded3a51dd6..cf2644e005 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js @@ -2,4 +2,13 @@ // For license information, please see license.txt frappe.ui.form.on('Quality Procedure', { + refresh: function(frm) { + frm.set_query("procedure","processes", (frm) =>{ + return { + filters: { + name: ["not in", [frm.parent_quality_procedure, frm.name]] + } + }; + }); + } }); \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json index 6df116c430..b3c0d94890 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_rename": 1, "autoname": "format:PRC-{quality_procedure_name}", "creation": "2018-10-06 00:06:29.756804", "doctype": "DocType", @@ -72,7 +73,7 @@ ], "is_tree": 1, "links": [], - "modified": "2020-03-18 18:09:29.371627", + "modified": "2020-06-17 17:25:03.434953", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure", diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index d29710dd8e..1952e57867 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -11,18 +11,18 @@ class QualityProcedure(NestedSet): nsm_parent_field = 'parent_quality_procedure' def before_save(self): - for process in self.processes: - if process.procedure: - doc = frappe.get_doc("Quality Procedure", process.procedure) - if doc.parent_quality_procedure: - frappe.throw(_("{0} already has a Parent Procedure {1}.").format(process.procedure, doc.parent_quality_procedure)) - self.is_group = 1 + self.check_for_incorrect_child() def on_update(self): self.set_parent() def after_insert(self): self.set_parent() + #if Child is Added through Tree View. + if self.parent_quality_procedure: + parent_quality_procedure = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) + parent_quality_procedure.append("processes", {"procedure": self.name}) + parent_quality_procedure.save() def on_trash(self): if self.parent_quality_procedure: @@ -42,11 +42,21 @@ class QualityProcedure(NestedSet): doc.save(ignore_permissions=True) def set_parent(self): + for process in self.processes: + # Set parent for only those children who don't have a parent + parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") + if not parent_quality_procedure and process.procedure: + frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name) + + def check_for_incorrect_child(self): for process in self.processes: if process.procedure: - doc = frappe.get_doc("Quality Procedure", process.procedure) - doc.parent_quality_procedure = self.name - doc.save(ignore_permissions=True) + # Check if any child process belongs to another parent. + parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") + if parent_quality_procedure and parent_quality_procedure != self.name: + frappe.throw(_("{0} already has a Parent Procedure {1}.".format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure))), + title=_("Invalid Child Procedure")) + self.is_group = 1 @frappe.whitelist() def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False): diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js index 6df6f656aa..ef48ab6c6e 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js @@ -16,6 +16,7 @@ frappe.treeview_settings["Quality Procedure"] = { }, ], breadcrumb: "Setup", + disable_add_node: true, root_label: "All Quality Procedures", get_tree_root: false, menu_items: [ diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json index 0a67fa505e..3925dbb8ac 100644 --- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json +++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json @@ -1,6 +1,8 @@ { + "actions": [], "creation": "2019-05-26 00:10:00.248885", "doctype": "DocType", + "editable_grid": 1, "engine": "InnoDB", "field_order": [ "process_description", @@ -23,7 +25,8 @@ } ], "istable": 1, - "modified": "2019-05-26 22:05:49.007189", + "links": [], + "modified": "2020-06-17 15:44:38.937915", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure Process", diff --git a/erpnext/regional/address_template/templates/india.html b/erpnext/regional/address_template/templates/india.html index ffb9d0547e..5d2329efff 100644 --- a/erpnext/regional/address_template/templates/india.html +++ b/erpnext/regional/address_template/templates/india.html @@ -1,7 +1,7 @@ {{ address_line1 }}
{% if address_line2 %}{{ address_line2 }}
{% endif -%}{{ city }}
{% if gst_state %}{{ gst_state }}{% endif -%} {% if gst_state_number %}, State Code: {{ gst_state_number }}
{% endif -%} -{% if pincode %}PIN: {{ pincode }}
{% endif -%} +{% if pincode %}Postal Code: {{ pincode }}
{% endif -%} {{ country }}
{% if phone %}Phone: {{ phone }}
{% endif -%} {% if fax %}Fax: {{ fax }}
{% endif -%} diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html index 35f9cf674c..888b2da48e 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html @@ -52,7 +52,7 @@ - (d) {{__("Inward Supplies(liable to reverse charge")}} + (d) {{__("Inward Supplies(liable to reverse charge)")}} {{ flt(data.sup_details.isup_rev.txval, 2) }} {{ flt(data.sup_details.isup_rev.iamt, 2) }} {{ flt(data.sup_details.isup_rev.camt, 2) }} diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 9e7a023926..619734ff26 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -158,7 +158,7 @@ class GSTR3BReport(Document): self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_det", ["Registered Regular"]) self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_zero", ["SEZ", "Deemed Export", "Overseas"]) - self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Registered Regular"], reverse_charge="Y") + self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Unregistered", "Overseas"], reverse_charge="Y") self.report_dict["sup_details"]["osup_nil_exmp"]["txval"] = flt(self.get_nil_rated_supply_value(), 2) self.set_itc_details(itc_details) @@ -192,31 +192,27 @@ class GSTR3BReport(Document): for d in self.report_dict["itc_elg"]["itc_avl"]: itc_type = itc_type_map.get(d["ty"]) - gst_category = "Registered Regular" + gst_category = ["Registered Regular"] if d["ty"] == 'ISRC': reverse_charge = "Y" + itc_type = 'All Other ITC' + gst_category = ['Unregistered', 'Overseas'] else: reverse_charge = "N" for account_head in self.account_heads: + for category in gst_category: + for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]: + d[key[0]] += flt(itc_details.get((category, itc_type, reverse_charge, account_head.get(key[1])), {}).get("amount"), 2) - d["iamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('igst_account')), {}).get("amount"), 2) - d["camt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cgst_account')), {}).get("amount"), 2) - d["samt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('sgst_account')), {}).get("amount"), 2) - d["csamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cess_account')), {}).get("amount"), 2) - - net_itc["iamt"] += flt(d["iamt"], 2) - net_itc["camt"] += flt(d["camt"], 2) - net_itc["samt"] += flt(d["samt"], 2) - net_itc["csamt"] += flt(d["csamt"], 2) + for key in ['iamt', 'camt', 'samt', 'csamt']: + net_itc[key] += flt(d[key], 2) for account_head in self.account_heads: itc_inelg = self.report_dict["itc_elg"]["itc_inelg"][1] - itc_inelg["iamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("igst_account")), {}).get("amount"), 2) - itc_inelg["camt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cgst_account")), {}).get("amount"), 2) - itc_inelg["samt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("sgst_account")), {}).get("amount"), 2) - itc_inelg["csamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cess_account")), {}).get("amount"), 2) + for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]: + itc_inelg[key[0]] = flt(itc_details.get(("Ineligible", "N", account_head.get(key[1])), {}).get("amount"), 2) def prepare_data(self, doctype, tax_details, supply_type, supply_category, gst_category_list, reverse_charge="N"): @@ -274,17 +270,16 @@ class GSTR3BReport(Document): """ #nosec .format(doctype = doctype), (self.month_no, self.year, reverse_charge, self.company, self.gst_details.get("gstin")))) - def get_itc_details(self, reverse_charge='N'): - + def get_itc_details(self): itc_amount = frappe.db.sql(""" select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t - where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s + where s.docstatus = 1 and t.parent = s.name and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s and s.company_gstin = %s group by t.account_head, s.gst_category, s.eligibility_for_itc """, - (reverse_charge, self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) + (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) itc_details = {} diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json index 59e955c23f..c1680c4b49 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -1,5 +1,4 @@ { - "actions": [], "creation": "2019-10-15 12:33:21.845329", "doctype": "DocType", "editable_grid": 1, @@ -92,8 +91,7 @@ "label": "Upload XML Invoices" } ], - "links": [], - "modified": "2019-12-10 16:37:26.793398", + "modified": "2020-05-25 21:32:49.064579", "modified_by": "Administrator", "module": "Regional", "name": "Import Supplier Invoice", diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 6b9567c0e5..31a7545a0d 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -64,7 +64,8 @@ class ImportSupplierInvoice(Document): "buying_price_list": self.default_buying_price_list } - if not invoices_args.get("invoice_no", ''): return + if not invoices_args.get("bill_no", ''): + frappe.throw(_("Numero has not set in the XML file")) supp_dict = get_supplier_details(file_content) invoices_args["destination_code"] = get_destination_code_from_file(file_content) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 8593966cc3..290694a789 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -60,6 +60,19 @@ def add_custom_roles_for_reports(): ] )).insert() + for report_name in ('Professional Tax Deductions', 'Provident Fund Deductions'): + + if not frappe.db.get_value('Custom Role', dict(report=report_name)): + frappe.get_doc(dict( + doctype='Custom Role', + report=report_name, + roles= [ + dict(role='HR User'), + dict(role='HR Manager'), + dict(role='Employee') + ] + )).insert() + def add_permissions(): for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'): add_permission(doctype, 'All', 0) @@ -402,10 +415,45 @@ def make_custom_fields(update=True): 'Purchase Receipt Item': [hsn_sac_field, nil_rated_exempt, is_non_gst], 'Purchase Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst], 'Material Request Item': [hsn_sac_field, nil_rated_exempt, is_non_gst], + 'Salary Component': [ + dict(fieldname= 'component_type', + label= 'Component Type', + fieldtype= 'Select', + insert_after= 'description', + options= "\nProvident Fund\nAdditional Provident Fund\nProvident Fund Loan\nProfessional Tax", + depends_on = 'eval:doc.type == "Deduction"' + ) + ], 'Employee': [ - dict(fieldname='ifsc_code', label='IFSC Code', - fieldtype='Data', insert_after='bank_ac_no', print_hide=1, - depends_on='eval:doc.salary_mode == "Bank"') + dict(fieldname='ifsc_code', + label='IFSC Code', + fieldtype='Data', + insert_after='bank_ac_no', + print_hide=1, + depends_on='eval:doc.salary_mode == "Bank"' + ), + dict( + fieldname = 'pan_number', + label = 'PAN Number', + fieldtype = 'Data', + insert_after = 'payroll_cost_center', + print_hide = 1 + ), + dict( + fieldname = 'micr_code', + label = 'MICR Code', + fieldtype = 'Data', + insert_after = 'ifsc_code', + print_hide = 1, + depends_on='eval:doc.salary_mode == "Bank"' + ), + dict( + fieldname = 'provident_fund_account', + label = 'Provident Fund Account', + fieldtype = 'Data', + insert_after = 'pan_number' + ) + ], 'Company': [ dict(fieldname='hra_section', label='HRA Settings', diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 732780a0a3..05ffa87f14 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -6,9 +6,11 @@ from erpnext.regional.india import states, state_numbers from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount from erpnext.controllers.accounts_controller import get_taxes_and_charges from erpnext.hr.utils import get_salary_assignment -from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip +from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.regional.india import number_state_mapping from six import string_types +from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.accounts.utils import get_account_currency def validate_gstin_for_india(doc, method): if hasattr(doc, 'gst_state') and doc.gst_state: @@ -615,8 +617,9 @@ def get_transport_details(data, doc): data.transDocDate = frappe.utils.formatdate(doc.lr_date, 'dd/mm/yyyy') if doc.gst_transporter_id: - validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') - data.transporterId = doc.gst_transporter_id + if doc.gst_transporter_id[0:2] != "88": + validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') + data.transporterId = doc.gst_transporter_id return data @@ -657,5 +660,53 @@ def get_gst_accounts(company, account_wise=False): elif val: gst_accounts[val] = acc - return gst_accounts + +def make_reverse_charge_entries(doc, method): + country = frappe.get_cached_value('Company', doc.company, 'country') + + if country != 'India': + return + + if doc.reverse_charge == 'Y': + gl_entries = [] + gst_accounts = get_gst_accounts(doc.company) + gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \ + + gst_accounts.get('igst_account') + + for tax in doc.get('taxes'): + if tax.category not in ("Total", "Valuation and Total"): + continue + + if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list: + account_currency = get_account_currency(tax.account_head) + + gl_entries.append(doc.get_gl_dict( + { + "account": tax.account_head, + "cost_center": tax.cost_center, + "posting_date": doc.posting_date, + "against": doc.supplier, + "credit": tax.base_tax_amount_after_discount_amount, + "credits_in_account_currency": tax.base_tax_amount_after_discount_amount \ + if account_currency==doc.company_currency \ + else tax.tax_amount_after_discount_amount + }, account_currency, item=tax) + ) + + gl_entries.append(doc.get_gl_dict( + { + "account": doc.credit_to if doc.doctype == 'Purchase Invoice' else doc.debit_to, + "cost_center": doc.cost_center, + "posting_date": doc.posting_date, + "party_type": 'Supplier', + "party": doc.supplier, + "against": tax.account_head, + "debit": tax.base_tax_amount_after_discount_amount, + "debit_in_account_currency": tax.base_tax_amount_after_discount_amount \ + if account_currency==doc.company_currency \ + else tax.tax_amount_after_discount_amount + }, account_currency, item=doc) + ) + + make_gl_entries(gl_entries) \ No newline at end of file diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py index 2d0ad66b0a..6ab73413df 100644 --- a/erpnext/regional/italy/setup.py +++ b/erpnext/regional/italy/setup.py @@ -7,11 +7,13 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields +from frappe.permissions import add_permission, update_permission_property from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons, mode_of_payment_codes, vat_collectability_options def setup(company=None, patch=True): make_custom_fields() setup_report() + add_permissions() def make_custom_fields(update=True): invoice_item_fields = [ @@ -200,3 +202,21 @@ def setup_report(): dict(role='Accounts Manager') ] )).insert() + +def add_permissions(): + doctype = 'Import Supplier Invoice' + add_permission(doctype, 'All', 0) + + for role in ('Accounts Manager', 'Accounts User','Purchase User', 'Auditor'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'print', 1) + update_permission_property(doctype, role, 0, 'report', 1) + + if role in ('Accounts Manager', 'Accounts User'): + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) + + update_permission_property(doctype, 'Accounts Manager', 0, 'delete', 1) + add_permission(doctype, 'Accounts Manager', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'write', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'create', 1) \ No newline at end of file diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index a8e40cc493..7fec94e740 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -8,17 +8,18 @@ Provide a report and downloadable CSV according to the German DATEV format. all required columns. Used to import the data into the DATEV Software. """ from __future__ import unicode_literals + import datetime import json -import zlib import zipfile import six +import frappe +import pandas as pd + +from frappe import _ from csv import QUOTE_NONNUMERIC from six import BytesIO from six import string_types -import frappe -from frappe import _ -import pandas as pd from .datev_constants import DataCategory from .datev_constants import Transactions from .datev_constants import DebtorsCreditors @@ -130,8 +131,10 @@ def get_customers(filters): SELECT acc.account_number as 'Konto', - cus.customer_name as 'Name (Adressatentyp Unternehmen)', - case cus.customer_type when 'Individual' then 1 when 'Company' then 2 else 0 end as 'Adressatentyp', + CASE cus.customer_type WHEN 'Company' THEN cus.customer_name ELSE null END as 'Name (Adressatentyp Unternehmen)', + CASE cus.customer_type WHEN 'Individual' THEN con.last_name ELSE null END as 'Name (Adressatentyp natürl. Person)', + CASE cus.customer_type WHEN 'Individual' THEN con.first_name ELSE null END as 'Vorname (Adressatentyp natürl. Person)', + CASE cus.customer_type WHEN 'Individual' THEN '1' WHEN 'Company' THEN '2' ELSE '0' end as 'Adressatentyp', adr.address_line1 as 'Straße', adr.pincode as 'Postleitzahl', adr.city as 'Ort', @@ -140,8 +143,7 @@ def get_customers(filters): con.email_id as 'E-Mail', coalesce(con.mobile_no, con.phone) as 'Telefon', cus.website as 'Internet', - cus.tax_id as 'Steuernummer', - ccl.credit_limit as 'Kreditlimit (Debitor)' + cus.tax_id as 'Steuernummer' FROM `tabParty Account` par @@ -160,10 +162,6 @@ def get_customers(filters): left join `tabContact` con on con.name = cus.customer_primary_contact - left join `tabCustomer Credit Limit` ccl - on ccl.parent = cus.name - and ccl.company = par.company - WHERE par.company = %(company)s AND par.parenttype = 'Customer'""", filters, as_dict=1) @@ -179,8 +177,10 @@ def get_suppliers(filters): SELECT acc.account_number as 'Konto', - sup.supplier_name as 'Name (Adressatentyp Unternehmen)', - case sup.supplier_type when 'Individual' then '1' when 'Company' then '2' else '0' end as 'Adressatentyp', + CASE sup.supplier_type WHEN 'Company' THEN sup.supplier_name ELSE null END as 'Name (Adressatentyp Unternehmen)', + CASE sup.supplier_type WHEN 'Individual' THEN con.last_name ELSE null END as 'Name (Adressatentyp natürl. Person)', + CASE sup.supplier_type WHEN 'Individual' THEN con.first_name ELSE null END as 'Vorname (Adressatentyp natürl. Person)', + CASE sup.supplier_type WHEN 'Individual' THEN '1' WHEN 'Company' THEN '2' ELSE '0' end as 'Adressatentyp', adr.address_line1 as 'Straße', adr.pincode as 'Postleitzahl', adr.city as 'Ort', @@ -226,9 +226,18 @@ def get_suppliers(filters): def get_account_names(filters): - return frappe.get_list("Account", - fields=["account_number as Konto", "name as Kontenbeschriftung"], - filters={"company": filters.get("company"), "is_group": "0"}) + return frappe.db.sql(""" + SELECT + + account_number as 'Konto', + LEFT(account_name, 40) as 'Kontenbeschriftung', + 'de-DE' as 'Sprach-ID' + + FROM `tabAccount` + WHERE company = %(company)s + AND is_group = 0 + AND account_number != '' + """, filters, as_dict=1) def get_datev_csv(data, filters, csv_class): @@ -287,9 +296,7 @@ def get_datev_csv(data, filters, csv_class): def get_header(filters, csv_class): - coa = frappe.get_value("Company", filters.get("company"), "chart_of_accounts") - description = filters.get("voucher_type", csv_class.FORMAT_NAME) - coa_used = "04" if "SKR04" in coa else ("03" if "SKR03" in coa else "") + description = filters.get('voucher_type', csv_class.FORMAT_NAME) header = [ # DATEV format @@ -316,19 +323,19 @@ def get_header(filters, csv_class): # J = Imported by -- stays empty '', # K = Tax consultant number (Beraternummer) - frappe.get_value("DATEV Settings", filters.get("company"), "consultant_number"), + filters.get('consultant_number', '0000000'), # L = Tax client number (Mandantennummer) - frappe.get_value("DATEV Settings", filters.get("company"), "client_number"), + filters.get('client_number', '00000'), # M = Start of the fiscal year (Wirtschaftsjahresbeginn) frappe.utils.formatdate(frappe.defaults.get_user_default("year_start_date"), "yyyyMMdd"), # N = Length of account numbers (Sachkontenlänge) - '4', + '%d' % filters.get('acc_len', 4), # O = Transaction batch start date (YYYYMMDD) - frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd"), + frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # P = Transaction batch end date (YYYYMMDD) - frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd"), + frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # Q = Description (for example, "Sales Invoice") Max. 30 chars - '"{}"'.format(_(description)), + '"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # R = Diktatkürzel '', # S = Buchungstyp @@ -343,12 +350,12 @@ def get_header(filters, csv_class): # 40 = Kalkulatorik # 11 = Reserviert # 12 = Reserviert - '0', + '0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # U = Festschreibung # TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1" '0', # V = Default currency, for example, "EUR" - '"%s"' % frappe.get_value("Company", filters.get("company"), "default_currency"), + '"%s"' % filters.get('default_currency', 'EUR') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # reserviert '', # Derivatskennzeichen @@ -358,7 +365,7 @@ def get_header(filters, csv_class): # reserviert '', # SKR - '"%s"' % coa_used, + '"%s"' % filters.get('skr', '04'), # Branchen-Lösungs-ID '', # reserviert @@ -389,6 +396,18 @@ def download_datev_csv(filters=None): validate(filters) + # set chart of accounts used + coa = frappe.get_value('Company', filters.get('company'), 'chart_of_accounts') + filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '') + + # set account number length + account_numbers = frappe.get_list('Account', fields=['account_number'], filters={'is_group': 0, 'account_number': ('!=', '')}) + filters['acc_len'] = max([len(a.account_number) for a in account_numbers]) + + filters['consultant_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'consultant_number') + filters['client_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'client_number') + filters['default_currency'] = frappe.get_value('Company', filters.get('company'), 'default_currency') + # This is where my zip will be written zip_buffer = BytesIO() # This is my zip file diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/report/datev/datev_constants.py index a059ed365a..e063703005 100644 --- a/erpnext/regional/report/datev/datev_constants.py +++ b/erpnext/regional/report/datev/datev_constants.py @@ -465,60 +465,71 @@ QUERY_REPORT_COLUMNS = [ "label": "Umsatz (ohne Soll/Haben-Kz)", "fieldname": "Umsatz (ohne Soll/Haben-Kz)", "fieldtype": "Currency", + "width": 100 }, { "label": "Soll/Haben-Kennzeichen", "fieldname": "Soll/Haben-Kennzeichen", "fieldtype": "Data", + "width": 100 }, { "label": "Konto", "fieldname": "Konto", "fieldtype": "Data", + "width": 100 }, { "label": "Gegenkonto (ohne BU-Schlüssel)", "fieldname": "Gegenkonto (ohne BU-Schlüssel)", "fieldtype": "Data", + "width": 100 }, { "label": "Belegdatum", "fieldname": "Belegdatum", "fieldtype": "Date", + "width": 100 }, { "label": "Belegfeld 1", "fieldname": "Belegfeld 1", "fieldtype": "Data", + "width": 150 }, { "label": "Buchungstext", "fieldname": "Buchungstext", "fieldtype": "Text", + "width": 300 }, { "label": "Beleginfo - Art 1", "fieldname": "Beleginfo - Art 1", "fieldtype": "Link", - "options": "DocType" + "options": "DocType", + "width": 100 }, { "label": "Beleginfo - Inhalt 1", "fieldname": "Beleginfo - Inhalt 1", "fieldtype": "Dynamic Link", - "options": "Beleginfo - Art 1" + "options": "Beleginfo - Art 1", + "width": 150 }, { "label": "Beleginfo - Art 2", "fieldname": "Beleginfo - Art 2", "fieldtype": "Link", - "options": "DocType" + "options": "DocType", + "width": 100 }, { "label": "Beleginfo - Inhalt 2", "fieldname": "Beleginfo - Inhalt 2", "fieldtype": "Dynamic Link", - "options": "Beleginfo - Art 2" + "options": "Beleginfo - Art 2", + "width": 150 } ] diff --git a/erpnext/regional/report/professional_tax_deductions/__init__.py b/erpnext/regional/report/professional_tax_deductions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js new file mode 100644 index 0000000000..29c7dbf43c --- /dev/null +++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Professional Tax Deductions"] = erpnext.salary_slip_deductions_report_filters; +}); \ No newline at end of file diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json new file mode 100644 index 0000000000..9938e9db52 --- /dev/null +++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json @@ -0,0 +1,20 @@ +{ + "add_total_row": 0, + "creation": "2020-06-02 00:37:44.537355", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 19:02:26.306348", + "modified_by": "Administrator", + "module": "Regional", + "name": "Professional Tax Deductions", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Professional Tax Deductions", + "report_type": "Script Report", + "roles": [] +} \ No newline at end of file diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py new file mode 100644 index 0000000000..acde68a942 --- /dev/null +++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py @@ -0,0 +1,72 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions + +def execute(filters=None): + data = get_data(filters) + columns = get_columns(filters) if len(data) else [] + + return columns, data + +def get_columns(filters): + columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 200 + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160 + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "width": 140 + } + ] + + return columns + +def get_data(filters): + + data = [] + + component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component` + where component_type = 'Professional Tax' """)) + + if not len(component_type_dict): + return [] + + conditions = get_conditions(filters) + + entry = frappe.db.sql(""" select sal.employee, sal.employee_name, ded.salary_component, ded.amount + from `tabSalary Slip` sal, `tabSalary Detail` ded + where sal.name = ded.parent + and ded.parentfield = 'deductions' + and ded.parenttype = 'Salary Slip' + and sal.docstatus = 1 %s + and ded.salary_component in (%s) + """ % (conditions , ", ".join(['%s']*len(component_type_dict))), tuple(component_type_dict.keys()), as_dict=1) + + for d in entry: + + employee = { + "employee": d.employee, + "employee_name": d.employee_name, + "amount": d.amount + } + + data.append(employee) + + return data \ No newline at end of file diff --git a/erpnext/regional/report/provident_fund_deductions/__init__.py b/erpnext/regional/report/provident_fund_deductions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js new file mode 100644 index 0000000000..b4dc28d177 --- /dev/null +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Provident Fund Deductions"] = erpnext.salary_slip_deductions_report_filters; +}); \ No newline at end of file diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json new file mode 100644 index 0000000000..e25d335f9b --- /dev/null +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json @@ -0,0 +1,20 @@ +{ + "add_total_row": 0, + "creation": "2020-06-01 23:44:07.919117", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 18:54:19.305763", + "modified_by": "Administrator", + "module": "Regional", + "name": "Provident Fund Deductions", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Provident Fund Deductions", + "report_type": "Script Report", + "roles": [] +} \ No newline at end of file diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py new file mode 100644 index 0000000000..597072c53a --- /dev/null +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py @@ -0,0 +1,168 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import getdate +from frappe import _ + +def execute(filters=None): + data = get_data(filters) + columns = get_columns(filters) if len(data) else [] + + return columns, data + +def get_columns(filters): + columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 200 + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160 + }, + { + "label": _("PF Account"), + "fieldname": "pf_account", + "fieldtype": "Data", + "width": 140 + }, + { + "label": _("PF Amount"), + "fieldname": "pf_amount", + "fieldtype": "Currency", + "width": 140 + }, + { + "label": _("Additional PF"), + "fieldname": "additional_pf", + "fieldtype": "Currency", + "width": 140 + }, + { + "label": _("PF Loan"), + "fieldname": "pf_loan", + "fieldtype": "Currency", + "width": 140 + }, + { + "label": _("Total"), + "fieldname": "total", + "fieldtype": "Currency", + "width": 140 + } + ] + + return columns + +def get_conditions(filters): + conditions = [""] + + if filters.get("department"): + conditions.append("sal.department = '%s' " % (filters["department"]) ) + + if filters.get("branch"): + conditions.append("sal.branch = '%s' " % (filters["branch"]) ) + + if filters.get("company"): + conditions.append("sal.company = '%s' " % (filters["company"])) + + if filters.get("month"): + conditions.append("month(sal.start_date) = '%s' " % (filters["month"])) + + if filters.get("year"): + conditions.append("year(start_date) = '%s' " % (filters["year"])) + + if filters.get("mode_of_payment"): + conditions.append("sal.mode_of_payment = '%s' " % (filters["mode_of_payment"])) + + return " and ".join(conditions) + +def prepare_data(entry,component_type_dict): + data_list = {} + + employee_account_dict = frappe._dict(frappe.db.sql(""" select name, provident_fund_account from `tabEmployee`""")) + + for d in entry: + + component_type = component_type_dict.get(d.salary_component) + + if data_list.get(d.name): + data_list[d.name][component_type] = d.amount + else: + data_list.setdefault(d.name,{ + "employee": d.employee, + "employee_name": d.employee_name, + "pf_account": employee_account_dict.get(d.employee), + component_type: d.amount + }) + + return data_list + +def get_data(filters): + data = [] + + conditions = get_conditions(filters) + + salary_slips = frappe.db.sql(""" select sal.name from `tabSalary Slip` sal + where docstatus = 1 %s + """ % (conditions), as_dict=1) + + component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component` + where component_type in ('Provident Fund', 'Additional Provident Fund', 'Provident Fund Loan')""")) + + if not len(component_type_dict): + return [] + + entry = frappe.db.sql(""" select sal.name, sal.employee, sal.employee_name, ded.salary_component, ded.amount + from `tabSalary Slip` sal, `tabSalary Detail` ded + where sal.name = ded.parent + and ded.parentfield = 'deductions' + and ded.parenttype = 'Salary Slip' + and sal.docstatus = 1 %s + and ded.salary_component in (%s) + """ % (conditions, ", ".join(['%s']*len(component_type_dict))), tuple(component_type_dict.keys()), as_dict=1) + + data_list = prepare_data(entry,component_type_dict) + + for d in salary_slips: + total = 0 + if data_list.get(d.name): + employee = { + "employee": data_list.get(d.name).get("employee"), + "employee_name": data_list.get(d.name).get("employee_name"), + "pf_account": data_list.get(d.name).get("pf_account") + } + + if data_list.get(d.name).get("Provident Fund"): + employee["pf_amount"] = data_list.get(d.name).get("Provident Fund") + total += data_list.get(d.name).get("Provident Fund") + + if data_list.get(d.name).get("Additional Provident Fund"): + employee["additional_pf"] = data_list.get(d.name).get("Additional Provident Fund") + total += data_list.get(d.name).get("Additional Provident Fund") + + if data_list.get(d.name).get("Provident Fund Loan"): + employee["pf_loan"] = data_list.get(d.name).get("Provident Fund Loan") + total += data_list.get(d.name).get("Provident Fund Loan") + + employee["total"] = total + + data.append(employee) + + return data + +@frappe.whitelist() +def get_years(): + year_list = frappe.db.sql_list("""select distinct YEAR(end_date) from `tabSalary Slip` ORDER BY YEAR(end_date) DESC""") + if not year_list: + year_list = [getdate().year] + + return "\n".join(str(year) for year in year_list) \ No newline at end of file diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index a01c6ceec3..772bbf5914 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -1,6 +1,8 @@ from __future__ import unicode_literals +import frappe from frappe.utils import flt from erpnext.controllers.taxes_and_totals import get_itemised_tax +from six import iteritems def update_itemised_tax_data(doc): if not doc.taxes: return @@ -9,7 +11,14 @@ def update_itemised_tax_data(doc): for row in doc.items: tax_rate = 0.0 - if itemised_tax.get(row.item_code): + item_tax_rate = frappe.parse_json(row.item_tax_rate) + + # First check if tax rate is present + # If not then look up in item_wise_tax_detail + if item_tax_rate: + for account, rate in iteritems(item_tax_rate): + tax_rate += rate + elif itemised_tax.get(row.item_code): tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()]) row.tax_rate = flt(tax_rate, row.precision("tax_rate")) diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py index 6d344025d2..cae28bee8b 100644 --- a/erpnext/regional/united_states/setup.py +++ b/erpnext/regional/united_states/setup.py @@ -9,14 +9,14 @@ def setup(company=None, patch=True): make_custom_fields() add_print_formats() -def make_custom_fields(): +def make_custom_fields(update=True): custom_fields = { 'Supplier': [ dict(fieldname='irs_1099', fieldtype='Check', insert_after='tax_id', label='Is IRS 1099 reporting required for supplier?') ] } - create_custom_fields(custom_fields) + create_custom_fields(custom_fields, update=update) def add_print_formats(): frappe.reload_doc("regional", "print_format", "irs_1099_form") diff --git a/erpnext/selling/dashboard_fixtures.py b/erpnext/selling/dashboard_fixtures.py new file mode 100644 index 0000000000..889cb88dce --- /dev/null +++ b/erpnext/selling/dashboard_fixtures.py @@ -0,0 +1,198 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json +from frappe import _ +from frappe.utils import nowdate +from erpnext.accounts.utils import get_fiscal_year + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +company = frappe.get_doc("Company", get_company_for_dashboards()) +fiscal_year = get_fiscal_year(nowdate(), as_dict=1) +fiscal_year_name = fiscal_year.get("name") +start_date = str(fiscal_year.get("year_start_date")) +end_date = str(fiscal_year.get("year_end_date")) + +def get_dashboards(): + return [{ + "name": "Selling", + "dashboard_name": "Selling", + "charts": [ + { "chart": "Sales Order Trends", "width": "Full"}, + { "chart": "Top Customers", "width": "Half"}, + { "chart": "Sales Order Analysis", "width": "Half"}, + { "chart": "Item-wise Annual Sales", "width": "Full"} + ], + "cards": [ + { "card": "Annual Sales"}, + { "card": "Sales Orders to Deliver"}, + { "card": "Sales Orders to Bill"}, + { "card": "Active Customers"} + ] + }] + +def get_charts(): + return [ + { + "name": "Sales Order Analysis", + "chart_name": _("Sales Order Analysis"), + "chart_type": "Report", + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "from_date": start_date, + "to_date": end_date + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Sales Order Analysis", + "type": "Donut" + }, + { + "name": "Item-wise Annual Sales", + "chart_name": _("Item-wise Annual Sales"), + "chart_type": "Report", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "from_date": start_date, + "to_date": end_date + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Item-wise Sales History", + "type": "Bar" + }, + { + "name": "Sales Order Trends", + "chart_name": _("Sales Order Trends"), + "chart_type": "Report", + "custom_options": json.dumps({ + "type": "line", + "axisOptions": {"shortenYAxisNumbers": 1}, + "tooltipOptions": {}, + "lineOptions": { + "regionFill": 1 + } + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "based_on": "Item" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Sales Order Trends", + "type": "Line" + }, + { + "name": "Top Customers", + "chart_name": _("Top Customers"), + "chart_type": "Report", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "based_on": "Customer" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Delivery Note Trends", + "type": "Bar" + } + ] + +def get_number_cards(): + return [ + { + "name": "Annual Sales", + "aggregate_function_based_on": "base_net_total", + "doctype": "Number Card", + "document_type": "Sales Order", + "filters_json": json.dumps([ + ["Sales Order", "transaction_date", "Between", [start_date, end_date], False], + ["Sales Order", "status", "not in", ["Draft", "Cancelled", "Closed", None], False], + ["Sales Order", "docstatus", "=", 1, False], + ["Sales Order", "company", "=", company.name, False] + ]), + "function": "Sum", + "is_public": 1, + "label": _("Annual Sales"), + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Sales Orders to Deliver", + "doctype": "Number Card", + "document_type": "Sales Order", + "filters_json": json.dumps([ + ["Sales Order", "status", "in", ["To Deliver and Bill", "To Deliver", None], False], + ["Sales Order", "docstatus", "=", 1, False], + ["Sales Order", "company", "=", company.name, False] + ]), + "function": "Count", + "is_public": 1, + "label": _("Sales Orders to Deliver"), + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Weekly" + }, + { + "name": "Sales Orders to Bill", + "doctype": "Number Card", + "document_type": "Sales Order", + "filters_json": json.dumps([ + ["Sales Order", "status", "in", ["To Deliver and Bill", "To Bill", None], False], + ["Sales Order", "docstatus", "=", 1, False], + ["Sales Order", "company", "=", company.name, False] + ]), + "function": "Count", + "is_public": 1, + "label": _("Sales Orders to Bill"), + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Weekly" + }, + { + "name": "Active Customers", + "doctype": "Number Card", + "document_type": "Customer", + "filters_json": json.dumps([["Customer", "disabled", "=", "0"]]), + "function": "Count", + "is_public": 1, + "label": "Active Customers", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + } + ] \ No newline at end of file diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index c32a7c8481..225238233a 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -1,5 +1,10 @@ { "cards": [ + { + "hidden": 0, + "label": "Selling", + "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]" + }, { "hidden": 0, "label": "Items and Pricing", @@ -10,29 +15,25 @@ "label": "Settings", "links": "[\n {\n \"description\": \"Default settings for selling transactions.\",\n \"label\": \"Selling Settings\",\n \"name\": \"Selling Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n }\n]" }, - { - "hidden": 0, - "label": "Other Reports", - "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Sales", - "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Invoices for Costumers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]" - }, { "hidden": 0, "label": "Key Reports", - "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n }\n]" + }, + { + "hidden": 0, + "label": "Other Reports", + "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", "charts": [ { - "chart_name": "Income", - "label": "Income" + "chart_name": "Sales Order Trends", + "label": "Sales Order Trends" } ], + "charts_label": "Selling ", "creation": "2020-01-28 11:49:12.092882", "developer_mode_only": 0, "disable_user_customization": 0, @@ -43,52 +44,49 @@ "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-05-28 13:46:08.314240", + "modified": "2020-06-29 19:26:35.139097", "modified_by": "Administrator", "module": "Selling", "name": "Selling", + "onboarding": "Selling", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, "shortcuts": [ { - "color": "#ffe8cd", - "format": "{} Draft", - "label": "Sales Invoice", - "link_to": "Sales Invoice", - "stats_filter": "{ \"status\": \"Draft\" }", + "color": "#cef6d1", + "format": "{} Available", + "label": "Item", + "link_to": "Item", + "stats_filter": "{\n \"disabled\":0\n}", "type": "DocType" }, { "color": "#ffe8cd", - "format": "{} To Deliver", + "format": "{} To Deliver", "label": "Sales Order", "link_to": "Sales Order", - "stats_filter": "{\"Status\": \"To Deliver and Bill\"}", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Deliver\", \"To Deliver and Bill\"]]\n}", "type": "DocType" }, { "color": "#cef6d1", "format": "{} Open", - "label": "Quotation", - "link_to": "Quotation", + "label": "Sales Analytics", + "link_to": "Sales Analytics", "stats_filter": "{ \"Status\": \"Open\" }", - "type": "DocType" - }, - { - "label": "Delivery Note", - "link_to": "Delivery Note", - "type": "DocType" - }, - { - "label": "Accounts Receivable", - "link_to": "Accounts Receivable", "type": "Report" }, { - "label": "Sales Register", - "link_to": "Sales Register", + "label": "Sales Order Analysis", + "link_to": "Sales Order Analysis", "type": "Report" + }, + { + "label": "Dashboard", + "link_to": "Selling", + "type": "Dashboard" } - ] + ], + "shortcuts_label": "Quick Access" } \ No newline at end of file diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index a6889e080d..d70c64fce4 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -3,16 +3,19 @@ from __future__ import unicode_literals import frappe +import json from frappe.model.naming import set_name_by_naming_series -from frappe import _, msgprint, throw +from frappe import _, msgprint import frappe.defaults -from frappe.utils import flt, cint, cstr, today +from frappe.utils import flt, cint, cstr, today, get_formatted_email from frappe.desk.reportview import build_match_conditions, get_filters_cond from erpnext.utilities.transaction_base import TransactionBase from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address from frappe.model.rename_doc import update_linked_doctypes from frappe.model.mapper import get_mapped_doc +from frappe.utils.user import get_users_with_role + class Customer(TransactionBase): def get_feed(self): @@ -336,6 +339,7 @@ def get_loyalty_programs(doc): return lp_details +@frappe.whitelist() def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): from erpnext.controllers.queries import get_fields @@ -378,10 +382,44 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, .format(customer, customer_outstanding, credit_limit)) # If not authorized person raise exception - credit_controller = frappe.db.get_value('Accounts Settings', None, 'credit_controller') - if not credit_controller or credit_controller not in frappe.get_roles(): - throw(_("Please contact to the user who have Sales Master Manager {0} role") - .format(" / " + credit_controller if credit_controller else "")) + credit_controller_role = frappe.db.get_single_value('Accounts Settings', 'credit_controller') + if not credit_controller_role or credit_controller_role not in frappe.get_roles(): + # form a list of emails for the credit controller users + credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager") + + # form a list of emails and names to show to the user + credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users] + + if not credit_controller_users: + frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer))) + + message = """Please contact any of the following users to extend the credit limits for {0}: +

""".format(customer, '
  • '.join(credit_controller_users)) + + # if the current user does not have permissions to override credit limit, + # prompt them to send out an email to the controller users + frappe.msgprint(message, + title="Notify", + raise_exception=1, + primary_action={ + 'label': 'Send Email', + 'server_action': 'erpnext.selling.doctype.customer.customer.send_emails', + 'args': { + 'customer': customer, + 'customer_outstanding': customer_outstanding, + 'credit_limit': credit_limit, + 'credit_controller_users_list': credit_controller_users + } + } + ) + +@frappe.whitelist() +def send_emails(args): + args = json.loads(args) + subject = (_("Credit limit reached for customer {0}").format(args.get('customer'))) + message = (_("Credit limit has been crossed for customer {0} ({1}/{2})") + .format(args.get('customer'), args.get('customer_outstanding'), args.get('credit_limit'))) + frappe.sendmail(recipients=[args.get('credit_controller_users_list')], subject=subject, message=message) def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None): # Outstanding based on GL Entries diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py index 85a2b209f6..7d1d372b11 100644 --- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py @@ -7,7 +7,7 @@ from __future__ import unicode_literals import frappe test_records = frappe.get_test_records('Product Bundle') -def make_product_bundle(parent, items): +def make_product_bundle(parent, items, qty=None): if frappe.db.exists("Product Bundle", parent): return frappe.get_doc("Product Bundle", parent) @@ -17,7 +17,7 @@ def make_product_bundle(parent, items): }) for item in items: - product_bundle.append("items", {"item_code": item, "qty": 1}) + product_bundle.append("items", {"item_code": item, "qty": qty or 1}) product_bundle.insert() diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 7cfec5a046..449a968a4f 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -99,6 +99,8 @@ class Quotation(SellingController): self.update_lead() def on_cancel(self): + if self.lost_reasons: + self.lost_reasons = [] super(Quotation, self).on_cancel() #update enquiry status @@ -197,9 +199,9 @@ def set_expired_status(): cond = "qo.docstatus = 1 and qo.status != 'Expired' and qo.valid_till < %s" # check if those QUO have SO against it so_against_quo = """ - SELECT + SELECT so.name FROM `tabSales Order` so, `tabSales Order Item` so_item - WHERE + WHERE so_item.docstatus = 1 and so.docstatus = 1 and so_item.parent = so.name and so_item.prevdoc_docname = qo.name""" diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index d50397cfad..59ae7b2323 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -48,6 +48,10 @@ "base_net_amount", "pricing_rules", "is_free_item", + "section_break_43", + "valuation_rate", + "column_break_45", + "gross_profit", "item_weight_details", "weight_per_unit", "total_weight", @@ -602,12 +606,40 @@ "label": "Against Blanket Order", "no_copy": 1, "print_hide": 1 + }, + { + "fieldname": "section_break_43", + "fieldtype": "Section Break" + }, + { + "fieldname": "valuation_rate", + "fieldtype": "Currency", + "label": "Valuation Rate", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, + { + "fieldname": "column_break_45", + "fieldtype": "Column Break" + }, + { + "fieldname": "gross_profit", + "fieldtype": "Currency", + "label": "Gross Profit", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-30 18:40:28.782720", + "modified": "2020-05-19 20:48:43.222229", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 05e4aa892b..ffb66354fa 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -868,7 +868,8 @@ def make_purchase_order(source_name, for_supplier=None, selected_items=[], targe ], "field_no_map": [ "rate", - "price_list_rate" + "price_list_rate", + "item_tax_template" ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index b8b0d404e5..accaa9c3b3 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals import frappe +import json from frappe.utils import flt, add_days, nowdate import frappe.permissions import unittest @@ -10,9 +11,10 @@ from erpnext.selling.doctype.sales_order.sales_order \ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.selling.doctype.sales_order.sales_order import make_work_orders from erpnext.controllers.accounts_controller import update_child_qty_rate -import json from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order +from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle +from erpnext.stock.doctype.item.test_item import make_item class TestSalesOrder(unittest.TestCase): def tearDown(self): @@ -400,6 +402,43 @@ class TestSalesOrder(unittest.TestCase): trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + def test_update_child_qty_rate_perm(self): + so = make_sales_order(item_code= "_Test Item", qty=4) + + user = 'test@example.com' + test_user = frappe.get_doc('User', user) + test_user.add_roles("Accounts User") + frappe.set_user(user) + + # update qty + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + + # add new item + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + frappe.set_user("Administrator") + + def test_update_child_qty_rate_product_bundle(self): + # test Update Items with product bundle + if not frappe.db.exists("Item", "_Product Bundle Item"): + bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0}) + bundle_item.append("item_defaults", { + "company": "_Test Company", + "default_warehouse": "_Test Warehouse - _TC"}) + bundle_item.save(ignore_permissions=True) + + make_item("_Packed Item", {"is_stock_item": 1}) + make_product_bundle("_Product Bundle Item", ["_Packed Item"], 2) + + so = make_sales_order(item_code = "_Test Item", warehouse=None) + + added_item = json.dumps([{"item_code" : "_Product Bundle Item", "rate" : 200, 'qty' : 2}]) + update_child_qty_rate('Sales Order', added_item, so.name) + + so.reload() + self.assertEqual(so.packed_items[0].qty, 4) + def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com") @@ -440,8 +479,6 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.CancelledLinkError, dn.submit) def test_service_type_product_bundle(self): - from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle - from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Service Product Bundle", {"is_stock_item": 0}) make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0}) make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0}) @@ -455,8 +492,6 @@ class TestSalesOrder(unittest.TestCase): self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items]) def test_mix_type_product_bundle(self): - from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle - from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Mix Product Bundle", {"is_stock_item": 0}) make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1}) make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0}) @@ -467,7 +502,6 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="") def test_auto_insert_price(self): - from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Item for Auto Price List", {"is_stock_item": 0}) frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1) @@ -502,7 +536,6 @@ class TestSalesOrder(unittest.TestCase): from erpnext.buying.doctype.purchase_order.purchase_order import update_status make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100) - from erpnext.stock.doctype.item.test_item import make_item po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "delivered_by_supplier": 1}) dn_item = make_item("_Test Regular Item", {"is_stock_item": 1}) @@ -697,7 +730,6 @@ class TestSalesOrder(unittest.TestCase): def test_serial_no_based_delivery(self): frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1) - from erpnext.stock.doctype.item.test_item import make_item item = make_item("_Reserved_Serialized_Item", {"is_stock_item": 1, "maintain_stock": 1, "has_serial_no": 1, @@ -818,7 +850,6 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.LinkExistsError, so_doc.cancel) def test_request_for_raw_materials(self): - from erpnext.stock.doctype.item.test_item import make_item item = make_item("_Test Finished Item", {"is_stock_item": 1, "maintain_stock": 1, "valuation_rate": 500, diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index e59349926e..eff17f8bc7 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -769,22 +769,23 @@ "label": "Against Blanket Order" }, { - "fieldname": "bom_no", - "fieldtype": "Link", - "label": "BOM No", - "no_copy": 1, - "options": "BOM", - "print_hide": 1 - }, - { - "fieldname": "manufacturing_section_section", - "fieldtype": "Section Break", - "label": "Manufacturing Section" - } + "fieldname": "bom_no", + "fieldtype": "Link", + "label": "BOM No", + "no_copy": 1, + "options": "BOM", + "print_hide": 1 + }, + { + "fieldname": "manufacturing_section_section", + "fieldtype": "Section Break", + "label": "Manufacturing Section" + } ], "idx": 1, "istable": 1, - "modified": "2020-05-15 18:13:43.006493", + "links": [], + "modified": "2020-05-29 20:54:32.309460", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.js b/erpnext/selling/doctype/selling_settings/selling_settings.js index cf6fb2806e..95a4243fb4 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.js +++ b/erpnext/selling/doctype/selling_settings/selling_settings.js @@ -6,3 +6,26 @@ frappe.ui.form.on('Selling Settings', { } }); + +frappe.tour['Selling Settings'] = [ + { + fieldname: "cust_master_name", + title: "Customer Naming By", + description: __("By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a ") + "Naming Series" + __(" choose the 'Naming Series' option."), + }, + { + fieldname: "selling_price_list", + title: "Default Selling Price List", + description: __("Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.") + }, + { + fieldname: "so_required", + title: "Sales Order Required for Sales Invoice & Delivery Note Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice or Delivery Note without creating a Sales Order first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Sales Order' checkbox in the Customer master.") + }, + { + fieldname: "dn_required", + title: "Delivery Note Required for Sales Invoice Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.") + } +]; \ No newline at end of file diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json index c04bfd281e..dcbc0748f7 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.json +++ b/erpnext/selling/doctype/selling_settings/selling_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2013-06-25 10:25:16", "description": "Settings for Selling Module", "doctype": "DocType", @@ -79,13 +80,13 @@ { "fieldname": "so_required", "fieldtype": "Select", - "label": "Sales Order Required", + "label": "Sales Order Required for Sales Invoice & Delivery Note Creation", "options": "No\nYes" }, { "fieldname": "dn_required", "fieldtype": "Select", - "label": "Delivery Note Required", + "label": "Delivery Note Required for Sales Invoice Creation", "options": "No\nYes" }, { @@ -137,7 +138,8 @@ "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-12-09 13:38:36.486298", + "links": [], + "modified": "2020-06-01 13:58:35.637858", "modified_by": "Administrator", "module": "Selling", "name": "Selling Settings", diff --git a/erpnext/selling/module_onboarding/selling/selling.json b/erpnext/selling/module_onboarding/selling/selling.json new file mode 100644 index 0000000000..10a33c9cf5 --- /dev/null +++ b/erpnext/selling/module_onboarding/selling/selling.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Sales Manager" + }, + { + "role": "Sales User" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-06-01 12:44:42.589930", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/selling", + "idx": 0, + "is_complete": 0, + "modified": "2020-06-01 13:35:16.100512", + "modified_by": "Administrator", + "module": "Selling", + "name": "Selling", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Selling" + }, + { + "step": "Create a Customer" + }, + { + "step": "Setup your Warehouse" + }, + { + "step": "Create a Product" + }, + { + "step": "Create a Quotation" + }, + { + "step": "Create your first Sales Order" + }, + { + "step": "Selling Settings" + } + ], + "subtitle": "Products, Sales, Analysis and more.", + "success_message": "The Selling Module is all set up!", + "title": "Let's Set Up the Selling Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json new file mode 100644 index 0000000000..5a403b06cf --- /dev/null +++ b/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:46:41.831517", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 13:16:19.731719", + "modified_by": "Administrator", + "name": "Create a Customer", + "owner": "Administrator", + "reference_document": "Customer", + "show_full_form": 0, + "title": "Create a Customer", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_a_product/create_a_product.json b/erpnext/selling/onboarding_step/create_a_product/create_a_product.json new file mode 100644 index 0000000000..d2068e167b --- /dev/null +++ b/erpnext/selling/onboarding_step/create_a_product/create_a_product.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:16:06.624554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:30:02.489949", + "modified_by": "Administrator", + "name": "Create a Product", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create a Product", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json b/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json new file mode 100644 index 0000000000..27253d15b6 --- /dev/null +++ b/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 13:34:58.958641", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 13:34:58.958641", + "modified_by": "Administrator", + "name": "Create a Quotation", + "owner": "Administrator", + "reference_document": "Quotation", + "show_full_form": 1, + "title": "Create a Quotation", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_product/create_product.json b/erpnext/selling/onboarding_step/create_product/create_product.json new file mode 100644 index 0000000000..0ffa30158b --- /dev/null +++ b/erpnext/selling/onboarding_step/create_product/create_product.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-05 16:42:31.476275", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 12:50:59.010439", + "modified_by": "Administrator", + "name": "Create Product", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create a Finished Good", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_your_first_sales_order/create_your_first_sales_order.json b/erpnext/selling/onboarding_step/create_your_first_sales_order/create_your_first_sales_order.json new file mode 100644 index 0000000000..5b601a7a90 --- /dev/null +++ b/erpnext/selling/onboarding_step/create_your_first_sales_order/create_your_first_sales_order.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 12:52:27.181841", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 12:52:27.181841", + "modified_by": "Administrator", + "name": "Create your first Sales Order", + "owner": "Administrator", + "reference_document": "Sales Order", + "show_full_form": 1, + "title": "Create your first Sales Order", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json b/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json new file mode 100644 index 0000000000..d21c1f4954 --- /dev/null +++ b/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json @@ -0,0 +1,19 @@ +{ + "action": "Watch Video", + "creation": "2020-06-01 12:44:32.089234", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 13:29:13.703177", + "modified_by": "Administrator", + "name": "Introduction to Selling", + "owner": "Administrator", + "show_full_form": 0, + "title": "Introduction to Selling", + "validate_action": 1, + "video_url": "https://youtu.be/1eP90MWoDQM" +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/selling_settings/selling_settings.json b/erpnext/selling/onboarding_step/selling_settings/selling_settings.json new file mode 100644 index 0000000000..7996d7b159 --- /dev/null +++ b/erpnext/selling/onboarding_step/selling_settings/selling_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-06-01 13:01:45.615189", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-06-01 13:04:14.980743", + "modified_by": "Administrator", + "name": "Selling Settings", + "owner": "Administrator", + "reference_document": "Selling Settings", + "show_full_form": 0, + "title": "Configure Selling Settings.", + "validate_action": 0 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json new file mode 100644 index 0000000000..557c905bd6 --- /dev/null +++ b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json @@ -0,0 +1,20 @@ +{ + "action": "Go to Page", + "creation": "2020-05-19 18:54:19.383397", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 18:54:19.383397", + "modified_by": "Administrator", + "name": "Setup your Warehouse", + "owner": "Administrator", + "path": "Tree/Warehouse", + "reference_document": "Warehouse", + "show_full_form": 0, + "title": "Setup your Warehouse", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index 88bd9c135d..f15f63d7bb 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -5,183 +5,183 @@ from __future__ import unicode_literals import calendar import frappe from frappe import _ -from frappe.utils import cint, cstr +from frappe.utils import cint, cstr, getdate def execute(filters=None): - common_columns = [ - { - 'label': _('New Customers'), - 'fieldname': 'new_customers', - 'fieldtype': 'Int', - 'default': 0, - 'width': 125 - }, - { - 'label': _('Repeat Customers'), - 'fieldname': 'repeat_customers', - 'fieldtype': 'Int', - 'default': 0, - 'width': 125 - }, - { - 'label': _('Total'), - 'fieldname': 'total', - 'fieldtype': 'Int', - 'default': 0, - 'width': 100 - }, - { - 'label': _('New Customer Revenue'), - 'fieldname': 'new_customer_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - }, - { - 'label': _('Repeat Customer Revenue'), - 'fieldname': 'repeat_customer_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - }, - { - 'label': _('Total Revenue'), - 'fieldname': 'total_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - } - ] - if filters.get('view_type') == 'Monthly': - return get_data_by_time(filters, common_columns) - else: - return get_data_by_territory(filters, common_columns) + common_columns = [ + { + 'label': _('New Customers'), + 'fieldname': 'new_customers', + 'fieldtype': 'Int', + 'default': 0, + 'width': 125 + }, + { + 'label': _('Repeat Customers'), + 'fieldname': 'repeat_customers', + 'fieldtype': 'Int', + 'default': 0, + 'width': 125 + }, + { + 'label': _('Total'), + 'fieldname': 'total', + 'fieldtype': 'Int', + 'default': 0, + 'width': 100 + }, + { + 'label': _('New Customer Revenue'), + 'fieldname': 'new_customer_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + }, + { + 'label': _('Repeat Customer Revenue'), + 'fieldname': 'repeat_customer_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + }, + { + 'label': _('Total Revenue'), + 'fieldname': 'total_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + } + ] + if filters.get('view_type') == 'Monthly': + return get_data_by_time(filters, common_columns) + else: + return get_data_by_territory(filters, common_columns) def get_data_by_time(filters, common_columns): - # key yyyy-mm - columns = [ - { - 'label': _('Year'), - 'fieldname': 'year', - 'fieldtype': 'Data', - 'width': 100 - }, - { - 'label': _('Month'), - 'fieldname': 'month', - 'fieldtype': 'Data', - 'width': 100 - }, - ] - columns += common_columns + # key yyyy-mm + columns = [ + { + 'label': _('Year'), + 'fieldname': 'year', + 'fieldtype': 'Data', + 'width': 100 + }, + { + 'label': _('Month'), + 'fieldname': 'month', + 'fieldtype': 'Data', + 'width': 100 + }, + ] + columns += common_columns - customers_in = get_customer_stats(filters) + customers_in = get_customer_stats(filters) - # time series - from_year, from_month, temp = filters.get('from_date').split('-') - to_year, to_month, temp = filters.get('to_date').split('-') + # time series + from_year, from_month, temp = filters.get('from_date').split('-') + to_year, to_month, temp = filters.get('to_date').split('-') - from_year, from_month, to_year, to_month = \ - cint(from_year), cint(from_month), cint(to_year), cint(to_month) + from_year, from_month, to_year, to_month = \ + cint(from_year), cint(from_month), cint(to_year), cint(to_month) - out = [] - for year in range(from_year, to_year+1): - for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13): - key = '{year}-{month:02d}'.format(year=year, month=month) - data = customers_in.get(key) - new = data['new'] if data else [0, 0.0] - repeat = data['repeat'] if data else [0, 0.0] - out.append({ - 'year': cstr(year), - 'month': calendar.month_name[month], - 'new_customers': new[0], - 'repeat_customers': repeat[0], - 'total': new[0] + repeat[0], - 'new_customer_revenue': new[1], - 'repeat_customer_revenue': repeat[1], - 'total_revenue': new[1] + repeat[1] - }) - return columns, out + out = [] + for year in range(from_year, to_year+1): + for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13): + key = '{year}-{month:02d}'.format(year=year, month=month) + data = customers_in.get(key) + new = data['new'] if data else [0, 0.0] + repeat = data['repeat'] if data else [0, 0.0] + out.append({ + 'year': cstr(year), + 'month': calendar.month_name[month], + 'new_customers': new[0], + 'repeat_customers': repeat[0], + 'total': new[0] + repeat[0], + 'new_customer_revenue': new[1], + 'repeat_customer_revenue': repeat[1], + 'total_revenue': new[1] + repeat[1] + }) + return columns, out def get_data_by_territory(filters, common_columns): - columns = [{ - 'label': 'Territory', - 'fieldname': 'territory', - 'fieldtype': 'Link', - 'options': 'Territory', - 'width': 150 - }] - columns += common_columns + columns = [{ + 'label': 'Territory', + 'fieldname': 'territory', + 'fieldtype': 'Link', + 'options': 'Territory', + 'width': 150 + }] + columns += common_columns - customers_in = get_customer_stats(filters, tree_view=True) + customers_in = get_customer_stats(filters, tree_view=True) - territory_dict = {} - for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1): - territory_dict.update({ - t.name: { - 'parent': t.parent_territory, - 'is_group': t.is_group - } - }) + territory_dict = {} + for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1): + territory_dict.update({ + t.name: { + 'parent': t.parent_territory, + 'is_group': t.is_group + } + }) - depth_map = frappe._dict() - for name, info in territory_dict.items(): - default = depth_map.get(info['parent']) + 1 if info['parent'] else 0 - depth_map.setdefault(name, default) + depth_map = frappe._dict() + for name, info in territory_dict.items(): + default = depth_map.get(info['parent']) + 1 if info['parent'] else 0 + depth_map.setdefault(name, default) - data = [] - for name, indent in depth_map.items(): - condition = customers_in.get(name) - new = customers_in[name]['new'] if condition else [0, 0.0] - repeat = customers_in[name]['repeat'] if condition else [0, 0.0] - temp = { - 'territory': name, - 'parent_territory': territory_dict[name]['parent'], - 'indent': indent, - 'new_customers': new[0], - 'repeat_customers': repeat[0], - 'total': new[0] + repeat[0], - 'new_customer_revenue': new[1], - 'repeat_customer_revenue': repeat[1], - 'total_revenue': new[1] + repeat[1], - 'bold': 0 if indent else 1 - } - data.append(temp) + data = [] + for name, indent in depth_map.items(): + condition = customers_in.get(name) + new = customers_in[name]['new'] if condition else [0, 0.0] + repeat = customers_in[name]['repeat'] if condition else [0, 0.0] + temp = { + 'territory': name, + 'parent_territory': territory_dict[name]['parent'], + 'indent': indent, + 'new_customers': new[0], + 'repeat_customers': repeat[0], + 'total': new[0] + repeat[0], + 'new_customer_revenue': new[1], + 'repeat_customer_revenue': repeat[1], + 'total_revenue': new[1] + repeat[1], + 'bold': 0 if indent else 1 + } + data.append(temp) - loop_data = sorted(data, key=lambda k: k['indent'], reverse=True) + loop_data = sorted(data, key=lambda k: k['indent'], reverse=True) - for ld in loop_data: - if ld['parent_territory']: - parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0] - for key in parent_data.keys(): - if key not in ['indent', 'territory', 'parent_territory', 'bold']: - parent_data[key] += ld[key] + for ld in loop_data: + if ld['parent_territory']: + parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0] + for key in parent_data.keys(): + if key not in ['indent', 'territory', 'parent_territory', 'bold']: + parent_data[key] += ld[key] - return columns, data, None, None, None, 1 + return columns, data, None, None, None, 1 def get_customer_stats(filters, tree_view=False): - """ Calculates number of new and repeated customers. """ - company_condition = '' - if filters.get('company'): - company_condition = ' and company=%(company)s' + """ Calculates number of new and repeated customers and revenue. """ + company_condition = '' + if filters.get('company'): + company_condition = ' and company=%(company)s' - customers = [] - customers_in = {} + customers = [] + customers_in = {} - for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` - where docstatus=1 and posting_date <= %(to_date)s and posting_date >= %(from_date)s - {company_condition} order by posting_date'''.format(company_condition=company_condition), - filters, as_dict=1): + for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` + where docstatus=1 and posting_date <= %(to_date)s + {company_condition} order by posting_date'''.format(company_condition=company_condition), + filters, as_dict=1): - key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') - customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) + key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') + new_or_repeat = 'new' if si.customer not in customers else 'repeat' + customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) - if not si.customer in customers: - customers_in[key]['new'][0] += 1 - customers_in[key]['new'][1] += si.base_grand_total - customers.append(si.customer) - else: - customers_in[key]['repeat'][0] += 1 - customers_in[key]['repeat'][1] += si.base_grand_total + # if filters.from_date <= si.posting_date.strftime('%Y-%m-%d'): + if getdate(filters.from_date) <= getdate(si.posting_date): + customers_in[key][new_or_repeat][0] += 1 + customers_in[key][new_or_repeat][1] += si.base_grand_total + if new_or_repeat == 'new': + customers.append(si.customer) - return customers_in + return customers_in diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js index daca2e3bd0..f47d67fe49 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js @@ -12,12 +12,6 @@ frappe.query_reports["Item-wise Sales History"] = { default: frappe.defaults.get_user_default("Company"), reqd: 1 }, - { - fieldname:"item_group", - label: __("Item Group"), - fieldtype: "Link", - options: "Item Group" - }, { fieldname:"from_date", reqd: 1, @@ -32,6 +26,38 @@ frappe.query_reports["Item-wise Sales History"] = { label: __("To Date"), fieldtype: "Date", }, + { + fieldname:"item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group" + }, + { + fieldname:"item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + get_query: () => { + return { + query: "erpnext.controllers.queries.item_query" + } + } + }, + { + fieldname:"customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer" + } + ], - ] + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["delivered_quantity", "billed_amount"]; + + if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + return value; + } }; \ No newline at end of file diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index 405004ece5..bd59be663a 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -11,7 +11,10 @@ def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - return columns, data + + chart_data = get_chart_data(data) + + return columns, data, None, chart_data def get_columns(filters): return [ @@ -96,7 +99,7 @@ def get_columns(filters): "label": _("Customer Group"), "fieldtype": "Link", "fieldname": "customer_group", - "options": "customer Group", + "options": "Customer Group", "width": 120 }, { @@ -181,6 +184,12 @@ def get_conditions(filters): if filters.get('to_date'): conditions += "AND so.transaction_date <= '%s'" %filters.to_date + if filters.get("item_code"): + conditions += "AND so_item.item_code = '%s'" %frappe.db.escape(filters.item_code) + + if filters.get("customer"): + conditions += "AND so.customer = '%s'" %frappe.db.escape(filters.customer) + return conditions def get_customer_details(): @@ -212,3 +221,34 @@ def get_sales_order_details(company_list, filters): AND so.company in ({0}) AND so.docstatus = 1 {1} """.format(','.join(["%s"] * len(company_list)), conditions), tuple(company_list), as_dict=1) + +def get_chart_data(data): + item_wise_sales_map = {} + labels, datapoints = [], [] + + for row in data: + item_key = row.get("item_code") + + if not item_key in item_wise_sales_map: + item_wise_sales_map[item_key] = 0 + + item_wise_sales_map[item_key] = flt(item_wise_sales_map[item_key]) + flt(row.get("amount")) + + item_wise_sales_map = { item: value for item, value in (sorted(item_wise_sales_map.items(), key = lambda i: i[1], reverse=True))} + + for key in item_wise_sales_map: + labels.append(key) + datapoints.append(item_wise_sales_map[key]) + + return { + "data" : { + "labels" : labels[:30], # show max of 30 items in chart + "datasets" : [ + { + "name" : _(" Total Sales Amount"), + "values" : datapoints[:30] + } + ] + }, + "type" : "bar" + } \ No newline at end of file diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.py b/erpnext/selling/report/quotation_trends/quotation_trends.py index 67375f98b3..968e2ff26f 100644 --- a/erpnext/selling/report/quotation_trends/quotation_trends.py +++ b/erpnext/selling/report/quotation_trends/quotation_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns, get_data def execute(filters=None): @@ -11,4 +12,48 @@ def execute(filters=None): conditions = get_columns(filters, "Quotation") data = get_data(filters, conditions) - return conditions["columns"], data \ No newline at end of file + chart_data = get_chart_data(data, conditions, filters) + + return conditions["columns"], data, None, chart_data + +def get_chart_data(data, conditions, filters): + if not (data and conditions): + return [] + + datapoints = [] + + start = 2 if filters.get("based_on") in ["Item", "Customer"] else 1 + if filters.get("group_by"): + start += 1 + + # fetch only periodic columns as labels + columns = conditions.get("columns")[start:-2][1::2] + labels = [column.split(':')[0] for column in columns] + datapoints = [0] * len(labels) + + for row in data: + # If group by filter, don't add first row of group (it's already summed) + if not row[start-1]: + continue + # Remove None values and compute only periodic data + row = [x if x else 0 for x in row[start:-2]] + row = row[1::2] + + for i in range(len(row)): + datapoints[i] += row[i] + + return { + "data" : { + "labels" : labels, + "datasets" : [ + { + "name" : _("{0}").format(filters.get("period")) + _(" Quoted Amount"), + "values" : datapoints + } + ] + }, + "type" : "line", + "lineOptions": { + "regionFill": 1 + } + } diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json index bf9edd6cd4..de5c3a6b2a 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.json +++ b/erpnext/selling/report/sales_analytics/sales_analytics.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "creation": "2018-09-21 12:46:29.451048", "disable_prepared_report": 0, "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2020-04-30 19:49:02.303320", + "modified": "2020-06-19 17:41:03.132101", "modified_by": "Administrator", "module": "Selling", "name": "Sales Analytics", diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 97d9322918..4d113c8e9e 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -23,7 +23,14 @@ class Analytics(object): self.get_columns() self.get_data() self.get_chart_data() - return self.columns, self.data, None, self.chart + + # Skipping total row for tree-view reports + skip_total_row = 0 + + if self.filters.tree_type in ["Supplier Group", "Item Group", "Customer Group", "Territory"]: + skip_total_row = 1 + + return self.columns, self.data, None, self.chart, None, skip_total_row def get_columns(self): self.columns = [{ @@ -194,9 +201,6 @@ class Analytics(object): def get_rows(self): self.data = [] self.get_periodic_data() - total_row = { - "entity": "Total", - } for entity, period_data in iteritems(self.entity_periodic_data): row = { @@ -210,9 +214,6 @@ class Analytics(object): row[scrub(period)] = amount total += amount - if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 - total_row[scrub(period)] += amount - row["total"] = total if self.filters.tree_type == "Item": @@ -220,8 +221,6 @@ class Analytics(object): self.data.append(row) - self.data.append(total_row) - def get_rows_by_group(self): self.get_periodic_data() out = [] diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py index 7e8501dcc1..4d81a1e4dd 100644 --- a/erpnext/selling/report/sales_analytics/test_analytics.py +++ b/erpnext/selling/report/sales_analytics/test_analytics.py @@ -33,21 +33,6 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ - { - 'entity': 'Total', - 'apr_2017': 0.0, - 'may_2017': 0.0, - 'jun_2017': 2000.0, - 'jul_2017': 1000.0, - 'aug_2017': 0.0, - 'sep_2017': 1500.0, - 'oct_2017': 1000.0, - 'nov_2017': 0.0, - 'dec_2017': 0.0, - 'jan_2018': 0.0, - 'feb_2018': 2000.0, - 'mar_2018': 0.0 - }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", @@ -149,21 +134,6 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ - { - 'entity': 'Total', - 'apr_2017': 0.0, - 'may_2017': 0.0, - 'jun_2017': 20.0, - 'jul_2017': 10.0, - 'aug_2017': 0.0, - 'sep_2017': 15.0, - 'oct_2017': 10.0, - 'nov_2017': 0.0, - 'dec_2017': 0.0, - 'jan_2018': 0.0, - 'feb_2018': 20.0, - 'mar_2018': 0.0 - }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", diff --git a/erpnext/selling/report/sales_order_analysis/__init__.py b/erpnext/selling/report/sales_order_analysis/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js new file mode 100644 index 0000000000..76a5bb51ca --- /dev/null +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js @@ -0,0 +1,85 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Sales Order Analysis"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname": "sales_order", + "label": __("Sales Order"), + "fieldtype": "MultiSelectList", + "width": "80", + "options": "Sales Order", + "get_data": function(txt) { + return frappe.db.get_link_options("Sales Order", txt); + }, + "get_query": () =>{ + return { + filters: { "docstatus": 1 } + } + } + }, + { + "fieldname": "status", + "label": __("Status"), + "fieldtype": "MultiSelectList", + "width": "80", + get_data: function(txt) { + let status = ["To Bill", "To Deliver", "To Deliver and Bill", "Completed"] + let options = [] + for (let option of status){ + options.push({ + "value": option, + "description": "" + }) + } + return options + } + }, + { + "fieldname": "group_by_so", + "label": __("Group by Sales Order"), + "fieldtype": "Check", + "default": 0 + } + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["delivered_qty", "billed_amount"]; + + if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + + if (column.fieldname == "delay" && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + return value; + } +}; diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.json b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.json new file mode 100644 index 0000000000..c0b1d9aa8c --- /dev/null +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.json @@ -0,0 +1,36 @@ +{ + "add_total_row": 1, + "creation": "2020-05-29 14:54:53.591445", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-29 14:54:53.591445", + "modified_by": "Administrator", + "module": "Selling", + "name": "Sales Order Analysis", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Order", + "report_name": "Sales Order Analysis", + "report_type": "Script Report", + "roles": [ + { + "role": "Sales User" + }, + { + "role": "Sales Manager" + }, + { + "role": "Maintenance User" + }, + { + "role": "Accounts User" + }, + { + "role": "Stock User" + } + ] +} \ No newline at end of file diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py new file mode 100644 index 0000000000..7e8e6e9e8b --- /dev/null +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -0,0 +1,279 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import copy +from frappe import _ +from frappe.utils import flt, date_diff, getdate + +def execute(filters=None): + if not filters: + return [], [], None, [] + + validate_filters(filters) + + columns = get_columns(filters) + conditions = get_conditions(filters) + data = get_data(conditions, filters) + + if not data: + return [], [], None, [] + + data, chart_data = prepare_data(data, filters) + + return columns, data, None, chart_data + +def validate_filters(filters): + from_date, to_date = filters.get("from_date"), filters.get("to_date") + + if not from_date and to_date: + frappe.throw(_("From and To Dates are required.")) + elif date_diff(to_date, from_date) < 0: + frappe.throw(_("To Date cannot be before From Date.")) + +def get_conditions(filters): + conditions = "" + if filters.get("from_date") and filters.get("to_date"): + conditions += " and so.transaction_date between %(from_date)s and %(to_date)s" + + if filters.get("company"): + conditions += " and so.company = %(company)s" + + if filters.get("sales_order"): + conditions += " and so.name in %(sales_order)s" + + if filters.get("status"): + conditions += " and so.status in %(status)s" + + return conditions + +def get_data(conditions, filters): + data = frappe.db.sql(""" + SELECT + so.transaction_date as date, + soi.delivery_date as delivery_date, + so.name as sales_order, + so.status, so.customer, soi.item_code, + DATEDIFF(CURDATE(), soi.delivery_date) as delay_days, + IF(so.status in ('Completed','To Bill'), 0, (SELECT delay_days)) as delay, + soi.qty, soi.delivered_qty, + (soi.qty - soi.delivered_qty) AS pending_qty, + IFNULL(sii.qty, 0) as billed_qty, + soi.base_amount as amount, + (soi.delivered_qty * soi.base_rate) as delivered_qty_amount, + (soi.billed_amt * IFNULL(so.conversion_rate, 1)) as billed_amount, + (soi.base_amount - (soi.billed_amt * IFNULL(so.conversion_rate, 1))) as pending_amount, + soi.warehouse as warehouse, + so.company, soi.name + FROM + `tabSales Order` so, + `tabSales Order Item` soi + LEFT JOIN `tabSales Invoice Item` sii + ON sii.so_detail = soi.name + WHERE + soi.parent = so.name + and so.status not in ('Stopped', 'Closed', 'On Hold') + and so.docstatus = 1 + {conditions} + GROUP BY soi.name + ORDER BY so.transaction_date ASC + """.format(conditions=conditions), filters, as_dict=1) + + return data + +def prepare_data(data, filters): + completed, pending = 0, 0 + + if filters.get("group_by_so"): + sales_order_map = {} + + for row in data: + # sum data for chart + completed += row["billed_amount"] + pending += row["pending_amount"] + + # prepare data for report view + row["qty_to_bill"] = flt(row["qty"]) - flt(row["billed_qty"]) + + row["delay"] = 0 if row["delay"] < 0 else row["delay"] + if filters.get("group_by_so"): + so_name = row["sales_order"] + + if not so_name in sales_order_map: + # create an entry + row_copy = copy.deepcopy(row) + sales_order_map[so_name] = row_copy + else: + # update existing entry + so_row = sales_order_map[so_name] + so_row["required_date"] = max(getdate(so_row["delivery_date"]), getdate(row["delivery_date"])) + so_row["delay"] = min(so_row["delay"], row["delay"]) + + # sum numeric columns + fields = ["qty", "delivered_qty", "pending_qty", "billed_qty", "qty_to_bill", "amount", + "delivered_qty_amount", "billed_amount", "pending_amount"] + for field in fields: + so_row[field] = flt(row[field]) + flt(so_row[field]) + + chart_data = prepare_chart_data(pending, completed) + + if filters.get("group_by_so"): + data = [] + for so in sales_order_map: + data.append(sales_order_map[so]) + return data, chart_data + + return data, chart_data + +def prepare_chart_data(pending, completed): + labels = ["Amount to Bill", "Billed Amount"] + + return { + "data" : { + "labels": labels, + "datasets": [ + {"values": [pending, completed]} + ] + }, + "type": 'donut', + "height": 300 + } + +def get_columns(filters): + columns = [ + { + "label":_("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 90 + }, + { + "label": _("Sales Order"), + "fieldname": "sales_order", + "fieldtype": "Link", + "options": "Sales Order", + "width": 160 + }, + { + "label":_("Status"), + "fieldname": "status", + "fieldtype": "Data", + "width": 130 + }, + { + "label": _("Customer"), + "fieldname": "customer", + "fieldtype": "Link", + "options": "Customer", + "width": 130 + }] + + if not filters.get("group_by_so"): + columns.append({ + "label":_("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 100 + }) + + columns.extend([ + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Delivered Qty"), + "fieldname": "delivered_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Qty to Deliver"), + "fieldname": "pending_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Billed Qty"), + "fieldname": "billed_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Qty to Bill"), + "fieldname": "qty_to_bill", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Billed Amount"), + "fieldname": "billed_amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Pending Amount"), + "fieldname": "pending_amount", + "fieldtype": "Currency", + "width": 130, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Amount Delivered"), + "fieldname": "delivered_qty_amount", + "fieldtype": "Currency", + "width": 100, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label":_("Delivery Date"), + "fieldname": "delivery_date", + "fieldtype": "Date", + "width": 120 + }, + { + "label": _("Delay (in Days)"), + "fieldname": "delay", + "fieldtype": "Data", + "width": 100 + } + ]) + if not filters.get("group_by_so"): + columns.append({ + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 100 + }) + columns.append({ + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 100 + }) + + + return columns \ No newline at end of file diff --git a/erpnext/selling/report/sales_order_trends/sales_order_trends.py b/erpnext/selling/report/sales_order_trends/sales_order_trends.py index c0a0f085d9..de7d3f2f77 100644 --- a/erpnext/selling/report/sales_order_trends/sales_order_trends.py +++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns,get_data def execute(filters=None): @@ -10,4 +11,48 @@ def execute(filters=None): data = [] conditions = get_columns(filters, "Sales Order") data = get_data(filters, conditions) - return conditions["columns"], data + chart_data = get_chart_data(data, conditions, filters) + + return conditions["columns"], data, None, chart_data + +def get_chart_data(data, conditions, filters): + if not (data and conditions): + return [] + + datapoints = [] + + start = 2 if filters.get("based_on") in ["Item", "Customer"] else 1 + if filters.get("group_by"): + start += 1 + + # fetch only periodic columns as labels + columns = conditions.get("columns")[start:-2][1::2] + labels = [column.split(':')[0] for column in columns] + datapoints = [0] * len(labels) + + for row in data: + # If group by filter, don't add first row of group (it's already summed) + if not row[start-1]: + continue + # Remove None values and compute only periodic data + row = [x if x else 0 for x in row[start:-2]] + row = row[1::2] + + for i in range(len(row)): + datapoints[i] += row[i] + + return { + "data" : { + "labels" : labels, + "datasets" : [ + { + "name" : _("{0}").format(filters.get("period")) + _(" Sales Value"), + "values" : datapoints + } + ] + }, + "type" : "line", + "lineOptions": { + "regionFill": 1 + } + } diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 875904fe6f..7ae5385a23 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -264,7 +264,10 @@ erpnext.company.setup_queries = function(frm) { ["expenses_included_in_valuation", {"root_type": "Expense", "account_type": "Expenses Included in Valuation"}], ["stock_received_but_not_billed", - {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}] + {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}], + ["service_received_but_not_billed", + {"root_type": "Liability", "account_type": "Service Received But Not Billed"}], + ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 020a93ff6a..c25edc5505 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -67,10 +67,12 @@ "payment_terms", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", + "enable_perpetual_inventory_for_non_stock_items", "default_inventory_account", "stock_adjustment_account", "column_break_32", "stock_received_but_not_billed", + "service_received_but_not_billed", "expenses_included_in_valuation", "fixed_asset_depreciation_settings", "accumulated_depreciation_account", @@ -723,6 +725,20 @@ "fieldtype": "Link", "label": "Default Buying Terms", "options": "Terms and Conditions" + }, + { + "fieldname": "service_received_but_not_billed", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Service Received But Not Billed", + "no_copy": 1, + "options": "Account" + }, + { + "default": "0", + "fieldname": "enable_perpetual_inventory_for_non_stock_items", + "fieldtype": "Check", + "label": "Enable Perpetual Inventory For Non Stock Items" } ], "icon": "fa fa-building", @@ -730,7 +746,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2020-03-21 18:09:53.534211", + "modified": "2020-06-20 11:38:43.178970", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 8bcaa28707..47b41a97ad 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -46,6 +46,7 @@ class Company(NestedSet): self.validate_currency() self.validate_coa_input() self.validate_perpetual_inventory() + self.validate_perpetual_inventory_for_non_stock_items() self.check_country_change() self.set_chart_of_accounts() self.validate_parent_company() @@ -182,6 +183,12 @@ class Company(NestedSet): frappe.msgprint(_("Set default inventory account for perpetual inventory"), alert=True, indicator='orange') + def validate_perpetual_inventory_for_non_stock_items(self): + if not self.get("__islocal"): + if cint(self.enable_perpetual_inventory_for_non_stock_items) == 1 and not self.service_received_but_not_billed: + frappe.throw(_("Set default {0} account for perpetual inventory for non stock items").format( + frappe.bold('Service Received But Not Billed'))) + def check_country_change(self): frappe.flags.country_change = False diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index c199a8e57f..44a5019120 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root customer group - if(!doc.parent_customer_group) { + if(!doc.parent_customer_group && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root customer group and cannot be edited.")); } else { @@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) { cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) { return { filters: { - 'is_group': 1 + 'is_group': 1, + 'name': ['!=', cur_frm.doc.customer_group_name] } } } diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py index f62613ea1b..68e1ccb635 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.py +++ b/erpnext/setup/doctype/customer_group/customer_group.py @@ -6,9 +6,12 @@ import frappe from frappe import _ -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of class CustomerGroup(NestedSet): nsm_parent_field = 'parent_customer_group' + def validate(self): + if not self.parent_customer_group: + self.parent_customer_group = get_root_of("Customer Group") def on_update(self): self.validate_name_with_customer() diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index 4d2d540bbc..7c0be3bff6 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -101,8 +101,7 @@ class EmailDigest(Document): if not context.purchase_order_list: frappe.throw(_("No items to be received are overdue")) - if not (context.events or context.todo_list or context.notifications or context.cards - or context.purchase_orders_items_overdue_list): + if not context: return None frappe.flags.ignore_account_permission = False diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js index df2223192b..9892dc3dcc 100644 --- a/erpnext/setup/doctype/item_group/item_group.js +++ b/erpnext/setup/doctype/item_group/item_group.js @@ -66,7 +66,7 @@ frappe.ui.form.on("Item Group", { set_root_readonly: function(frm) { // read-only for root item group frm.set_intro(""); - if(!frm.doc.parent_item_group) { + if(!frm.doc.parent_item_group && !frm.doc.__islocal) { frm.set_read_only(); frm.set_intro(__("This is a root item group and cannot be edited."), true); } diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js index 89ca4a9dd7..8f7593d6ee 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.js +++ b/erpnext/setup/doctype/sales_person/sales_person.js @@ -19,7 +19,7 @@ frappe.ui.form.on('Sales Person', { } } }; - + frm.make_methods = { 'Sales Order': () => frappe.new_doc("Sales Order") .then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name})) @@ -33,7 +33,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root - if(!doc.parent_sales_person) { + if(!doc.parent_sales_person && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root sales person and cannot be edited.")); } else { diff --git a/erpnext/setup/doctype/sales_person/sales_person.py b/erpnext/setup/doctype/sales_person/sales_person.py index 3379534cf8..19c2e5b954 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.py +++ b/erpnext/setup/doctype/sales_person/sales_person.py @@ -5,13 +5,16 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of from erpnext import get_default_currency class SalesPerson(NestedSet): nsm_parent_field = 'parent_sales_person' def validate(self): + if not self.parent_sales_person: + self.parent_sales_person = get_root_of("Sales Person") + for d in self.get('targets') or []: if not flt(d.target_qty) and not flt(d.target_amount): frappe.throw(_("Either target qty or target amount is mandatory.")) diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index ac5bda6e2c..e75030d441 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root customer group - if(!doc.parent_supplier_group) { + if(!doc.parent_supplier_group && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root supplier group and cannot be edited.")); } else { @@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) { cur_frm.fields_dict['parent_supplier_group'].get_query = function() { return { filters: { - 'is_group': 1 + 'is_group': 1, + 'name': ['!=', cur_frm.doc.supplier_group_name] } }; }; diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json index aba6a791a4..28d1d16a05 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:title", @@ -49,7 +50,7 @@ "fieldname": "terms_and_conditions_help", "fieldtype": "HTML", "label": "Terms and Conditions Help", - "options": "

    Standard Terms and Conditions Example

    \n\n
    Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
    \n\n

    How to get fieldnames

    \n\n

    The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

    \n\n

    Templating

    \n\n

    Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.

    " + "options": "

    Standard Terms and Conditions Example

    \n\n
    Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
    \n\n

    How to get fieldnames

    \n\n

    The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

    \n\n

    Templating

    \n\n

    Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

    " }, { "fieldname": "applicable_modules_section", @@ -81,7 +82,8 @@ ], "icon": "icon-legal", "idx": 1, - "modified": "2019-07-04 13:31:30.393425", + "links": [], + "modified": "2020-06-16 22:54:38.094844", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", diff --git a/erpnext/setup/doctype/territory/territory.js b/erpnext/setup/doctype/territory/territory.js index 1eb9958ce7..ceec47ae8c 100644 --- a/erpnext/setup/doctype/territory/territory.js +++ b/erpnext/setup/doctype/territory/territory.js @@ -20,7 +20,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root territory - if(!doc.parent_territory) { + if(!doc.parent_territory && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root territory and cannot be edited.")); } else { diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py index 808b5386ab..05e8f666cf 100644 --- a/erpnext/setup/doctype/territory/territory.py +++ b/erpnext/setup/doctype/territory/territory.py @@ -6,12 +6,14 @@ import frappe from frappe.utils import flt from frappe import _ -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of class Territory(NestedSet): nsm_parent_field = 'parent_territory' def validate(self): + if not self.parent_territory: + self.parent_territory = get_root_of("Territory") for d in self.get('targets') or []: if not flt(d.target_qty) and not flt(d.target_amount): diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index e666a41f30..74ff0ecfd8 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -105,3 +105,4 @@ def add_company_to_session_defaults(): "ref_doctype": "Company" }) settings.save() + diff --git a/erpnext/setup/setup_wizard/data/uom_conversion_data.json b/erpnext/setup/setup_wizard/data/uom_conversion_data.json index 174ecd5903..27a917d9db 100644 --- a/erpnext/setup/setup_wizard/data/uom_conversion_data.json +++ b/erpnext/setup/setup_wizard/data/uom_conversion_data.json @@ -1571,5 +1571,19 @@ "to_uom": "Parts Per Million", "abbr": "ppm", "value": "10000" + }, + { + "category": "Mass", + "from_uom": "Pound", + "to_uom": "Ounce", + "abbr": "oz", + "value": "16" + }, + { + "category": "Mass", + "from_uom": "Gram", + "to_uom": "Ounce", + "abbr": "oz", + "value": "0.035274" } ] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 8bb0a0529d..ad063cfc9d 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -50,7 +50,7 @@ def install(country=None): 'is_group': 0, 'parent_item_group': _('All Item Groups') }, # salary component - {'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax'), 'type': 'Deduction'}, + {'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax'), 'type': 'Deduction', 'is_income_tax_component': 1}, {'doctype': 'Salary Component', 'salary_component': _('Basic'), 'description': _('Basic'), 'type': 'Earning'}, {'doctype': 'Salary Component', 'salary_component': _('Arrear'), 'description': _('Arrear'), 'type': 'Earning'}, {'doctype': 'Salary Component', 'salary_component': _('Leave Encashment'), 'description': _('Leave Encashment'), 'type': 'Earning'}, @@ -336,13 +336,14 @@ def add_uom_data(): "category_name": _(d.get("category")) }).insert(ignore_permissions=True) - uom_conversion = frappe.get_doc({ - "doctype": "UOM Conversion Factor", - "category": _(d.get("category")), - "from_uom": _(d.get("from_uom")), - "to_uom": _(d.get("to_uom")), - "value": d.get("value") - }).insert(ignore_permissions=True) + if not frappe.db.exists("UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}): + uom_conversion = frappe.get_doc({ + "doctype": "UOM Conversion Factor", + "category": _(d.get("category")), + "from_uom": _(d.get("from_uom")), + "to_uom": _(d.get("to_uom")), + "value": d.get("value") + }).insert(ignore_permissions=True) def add_market_segments(): records = [ diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 4ac546e82c..a7e8388be9 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -42,9 +42,9 @@ def get_cart_quotation(doc=None): return { "doc": decorate_quotation_doc(doc), - "shipping_addresses": [{"name": address.name, "display": address.display} + "shipping_addresses": [{"name": address.name, "title": address.address_title, "display": address.display} for address in addresses if address.address_type == "Shipping"], - "billing_addresses": [{"name": address.name, "display": address.display} + "billing_addresses": [{"name": address.name, "title": address.address_title, "display": address.display} for address in addresses if address.address_type == "Billing"], "shipping_rules": get_applicable_shipping_rules(party), "cart_settings": frappe.get_cached_doc("Shopping Cart Settings") @@ -78,8 +78,10 @@ def place_order(): if is_stock_item: item_stock = get_qty_in_stock(item.item_code, "website_warehouse") + if not cint(item_stock.in_stock): + throw(_("{1} Not in Stock").format(item.item_code)) if item.qty > item_stock.stock_qty[0][0]: - throw(_("Only {0} in stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) + throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) sales_order.flags.ignore_permissions = True sales_order.insert() @@ -319,7 +321,7 @@ def apply_cart_settings(party=None, quotation=None): def set_price_list_and_rate(quotation, cart_settings): """set price list based on billing territory""" - _set_price_list(quotation, cart_settings) + _set_price_list(cart_settings, quotation) # reset values quotation.price_list_currency = quotation.currency = \ @@ -334,23 +336,24 @@ def set_price_list_and_rate(quotation, cart_settings): # set it in cookies for using in product page frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list) -def _set_price_list(quotation, cart_settings): +def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list - - # check if customer price list exists + party_name = quotation.get("party_name") if quotation else get_party().get("name") selling_price_list = None - if quotation.party_name: - selling_price_list = frappe.db.get_value('Customer', quotation.party_name, 'default_price_list') - # else check for territory based price list + # check if default customer price list exists + if party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + # check default price list in shopping cart if not selling_price_list: selling_price_list = cart_settings.price_list - if not selling_price_list and quotation.party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name)) + if quotation: + quotation.selling_price_list = selling_price_list - quotation.selling_price_list = selling_price_list + return selling_price_list def set_taxes(quotation, cart_settings): """set taxes based on billing territory""" diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 21ee335125..7c08f5b5b2 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from erpnext.shopping_cart.cart import _get_cart_quotation +from erpnext.shopping_cart.cart import _get_cart_quotation, _set_price_list from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings \ import get_shopping_cart_settings, show_quantity_in_website from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status @@ -21,9 +21,11 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if not skip_quotation_creation: cart_quotation = _get_cart_quotation() + selling_price_list = cart_quotation.get("selling_price_list") if cart_quotation else _set_price_list(cart_settings, None) + price = get_price( item_code, - cart_quotation.selling_price_list, + selling_price_list, cart_settings.default_customer_group, cart_settings.company ) @@ -42,7 +44,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if product_info["price"]: if frappe.session.user != "Guest": - item = cart_quotation.get({"item_code": item_code}) + item = cart_quotation.get({"item_code": item_code}) if cart_quotation else None if item: product_info["qty"] = item[0].qty diff --git a/erpnext/startup/filters.py b/erpnext/startup/filters.py new file mode 100644 index 0000000000..a99e49b491 --- /dev/null +++ b/erpnext/startup/filters.py @@ -0,0 +1,14 @@ + +import frappe + +def get_filters_config(): + filters_config = { + "fiscal year": { + "label": "Fiscal Year", + "get_field": "erpnext.accounts.utils.get_fiscal_year_filter_field", + "valid_for_fieldtypes": ["Date", "Datetime", "DateRange"], + "depends_on": "company", + } + } + + return filters_config \ No newline at end of file diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py index 0f1fd128f0..7625b1ad28 100644 --- a/erpnext/stock/dashboard_fixtures.py +++ b/erpnext/stock/dashboard_fixtures.py @@ -5,31 +5,26 @@ import frappe import json from frappe import _ from frappe.utils import nowdate -from erpnext.accounts.utils import get_fiscal_year +from erpnext.accounts.dashboard_fixtures import _get_fiscal_year +from erpnext.buying.dashboard_fixtures import get_company_for_dashboards def get_data(): + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + + company = frappe.get_doc("Company", get_company_for_dashboards()) + fiscal_year_name = fiscal_year.get("name") + start_date = str(fiscal_year.get("year_start_date")) + end_date = str(fiscal_year.get("year_end_date")) + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts(company, fiscal_year_name, start_date, end_date), + "number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date), }) -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None - -company = frappe.get_doc("Company", get_company_for_dashboards()) -fiscal_year = get_fiscal_year(nowdate(), as_dict=1) -fiscal_year_name = fiscal_year.get("name") -start_date = str(fiscal_year.get("year_start_date")) -end_date = str(fiscal_year.get("year_end_date")) - def get_dashboards(): return [{ "name": "Stock", @@ -48,7 +43,7 @@ def get_dashboards(): ] }] -def get_charts(): +def get_charts(company, fiscal_year_name, start_date, end_date): return [ { "doctype": "Dashboard Chart", @@ -133,7 +128,7 @@ def get_charts(): } ] -def get_number_cards(): +def get_number_cards(company, fiscal_year_name, start_date, end_date): return [ { "name": "Total Active Items", diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 9404292c04..1bf81f7f0e 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -33,7 +33,7 @@ { "hidden": 0, "label": "Key Reports", - "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -58,7 +58,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-05-27 20:38:25.255323", + "modified": "2020-05-30 17:32:11.062681", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 3436a5d013..a75ee67ec4 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -597,7 +597,7 @@ class Item(WebsiteGenerator): def stock_ledger_created(self): if not hasattr(self, '_stock_ledger_created'): self._stock_ledger_created = len(frappe.db.sql("""select name from `tabStock Ledger Entry` - where item_code = %s limit 1""", self.name)) + where item_code = %s and is_cancelled = 0 limit 1""", self.name)) return self._stock_ledger_created def validate_name_with_item_group(self): @@ -883,7 +883,12 @@ class Item(WebsiteGenerator): linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"] for doctype in linked_doctypes: - if frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \ + if doctype in ("Purchase Invoice Item", "Sales Invoice Item",): + # If Invoice has Stock impact, only then consider it. + if self.stock_ledger_created(): + return True + + elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \ frappe.db.get_value("Production Order", filters={"production_item": self.name, "docstatus": 1}): return True diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py index 71b998fb95..2f75bbd97c 100644 --- a/erpnext/stock/doctype/item_attribute/item_attribute.py +++ b/erpnext/stock/doctype/item_attribute/item_attribute.py @@ -34,7 +34,7 @@ class ItemAttribute(Document): if self.numeric_values: validate_is_incremental(self, self.name, item.value, item.name) else: - validate_item_attribute_value(attributes_list, self.name, item.value, item.name) + validate_item_attribute_value(attributes_list, self.name, item.value, item.name, from_variant=False) def validate_numeric(self): if self.numeric_values: diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 3562181e25..3a8deb6d25 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -18,7 +18,7 @@ frappe.ui.form.on('Material Request', { // formatter for material request item frm.set_indicator_formatter('item_code', - function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; }); + function(doc) { return (doc.stock_qty<=doc.ordered_qty) ? "green" : "orange"; }); frm.set_query("item_code", "items", function() { return { diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index d46b98b461..3a5ef76980 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -31,10 +31,16 @@ frappe.ui.form.on('Pick List', { }; }); frm.set_query('item_code', 'locations', () => { + return erpnext.queries.item({ "is_stock_item": 1 }); + }); + frm.set_query('batch_no', 'locations', (frm, cdt, cdn) => { + const row = locals[cdt][cdn]; return { + query: 'erpnext.controllers.queries.get_batch_no', filters: { - is_stock_item: 1 - } + item_code: row.item_code, + warehouse: row.warehouse + }, }; }); }, diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 1f8d009f92..4b8b594ed9 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -24,6 +24,9 @@ class PickList(Document): for item in self.locations: if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'): continue + if not item.serial_no: + frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format( + frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)))) if len(item.serial_no.split('\n')) == item.picked_qty: continue frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity') @@ -116,11 +119,13 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): if item_location.serial_no: serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)]) + auto_set_serial_no = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo") + locations.append(frappe._dict({ 'qty': qty, 'stock_qty': stock_qty, 'warehouse': item_location.warehouse, - 'serial_no': serial_nos, + 'serial_no': serial_nos if auto_set_serial_no else item_doc.serial_no, 'batch_no': item_location.batch_no })) @@ -203,6 +208,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re sle.batch_no = batch.name and sle.`item_code`=%(item_code)s and sle.`company` = %(company)s + and batch.disabled = 0 and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s {warehouse_condition} GROUP BY @@ -468,4 +474,4 @@ def update_common_item_properties(item, location): item.material_request = location.material_request item.serial_no = location.serial_no item.batch_no = location.batch_no - item.material_request_item = location.material_request_item \ No newline at end of file + item.material_request_item = location.material_request_item diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index e9568eeacc..50c18f6282 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -25,7 +25,7 @@ frappe.ui.form.on("Purchase Receipt", { frm.custom_make_buttons = { 'Stock Entry': 'Return', - 'Purchase Invoice': 'Invoice' + 'Purchase Invoice': 'Purchase Invoice' }; frm.set_query("expense_account", "items", function() { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 467a206d18..44d5f69028 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -32,6 +32,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -130,13 +132,17 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break0", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -147,7 +153,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -159,7 +167,9 @@ "options": "MAT-PRE-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -174,6 +184,8 @@ "print_width": "150px", "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -184,18 +196,24 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_delivery_note", "fieldtype": "Data", - "label": "Supplier Delivery Note" + "label": "Supplier Delivery Note", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -210,6 +228,8 @@ "print_width": "100px", "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -223,6 +243,8 @@ "print_hide": 1, "print_width": "100px", "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -231,7 +253,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -245,6 +269,8 @@ "print_width": "150px", "remember_last_selected_value": 1, "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -254,7 +280,9 @@ "label": "Is Return", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_return", @@ -264,46 +292,60 @@ "no_copy": 1, "options": "Purchase Receipt", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", @@ -311,32 +353,42 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -346,7 +398,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Rate at which supplier's currency is converted to company's base currency", @@ -357,13 +411,17 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -371,7 +429,9 @@ "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "buying_price_list", @@ -380,7 +440,9 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "buying_price_list", @@ -388,7 +450,9 @@ "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -397,11 +461,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Accepted Warehouse' in each row of the items table.", @@ -409,7 +477,9 @@ "fieldtype": "Link", "label": "Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Rejected Warehouse' in each row of the items table.", @@ -420,11 +490,15 @@ "oldfieldname": "rejected_warehouse", "oldfieldtype": "Link", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -434,7 +508,9 @@ "oldfieldname": "is_subcontracted", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", @@ -447,13 +523,17 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -463,20 +543,26 @@ "oldfieldname": "purchase_receipt_details", "oldfieldtype": "Table", "options": "Purchase Receipt Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "supplied_items", @@ -485,7 +571,9 @@ "label": "Get Current Stock", "oldfieldtype": "Button", "options": "get_current_stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -496,7 +584,9 @@ "oldfieldtype": "Section Break", "options": "fa fa-table", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", @@ -507,18 +597,24 @@ "oldfieldtype": "Table", "options": "Purchase Receipt Item Supplied", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break0", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -526,7 +622,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -539,18 +637,24 @@ "print_width": "150px", "read_only": 1, "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { "fieldname": "column_break_27", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -560,42 +664,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Add / Edit Taxes and Charges", "fieldname": "taxes_charges_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_col", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", - "options": "Shipping Rule" + "options": "Shipping Rule", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -604,7 +722,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -612,13 +732,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -627,13 +751,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -643,7 +771,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -653,7 +783,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -663,12 +795,16 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break3", "fieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -679,7 +815,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -689,7 +827,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -697,14 +837,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_42", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -712,7 +856,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -720,28 +866,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_44", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -751,7 +907,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -760,7 +918,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -769,7 +929,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounded_total", @@ -779,11 +941,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "grand_total", @@ -793,7 +959,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -802,7 +970,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -812,7 +982,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -821,13 +993,17 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -836,7 +1012,9 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -845,14 +1023,18 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -861,7 +1043,9 @@ "label": "Bill No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -870,7 +1054,9 @@ "label": "Bill Date", "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -878,7 +1064,9 @@ "fieldtype": "Section Break", "label": "More Information", "oldfieldtype": "Section Break", - "options": "fa fa-file-text" + "options": "fa fa-file-text", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -895,6 +1083,8 @@ "read_only": 1, "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -910,6 +1100,8 @@ "print_hide": 1, "print_width": "150px", "read_only": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -919,7 +1111,9 @@ "label": "Range", "oldfieldname": "range", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break4", @@ -927,6 +1121,8 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -935,12 +1131,16 @@ "label": "% Amount Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "subscription_detail", "fieldtype": "Section Break", - "label": "Auto Repeat Detail" + "label": "Auto Repeat Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -949,13 +1149,17 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -963,7 +1167,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -975,13 +1181,17 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -989,11 +1199,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_details", @@ -1004,6 +1218,8 @@ "options": "
    Other Details
    ", "print_hide": 1, "print_width": "30%", + "show_days": 1, + "show_seconds": 1, "width": "30%" }, { @@ -1011,13 +1227,17 @@ "fieldtype": "Small Text", "label": "Instructions", "oldfieldname": "instructions", - "oldfieldtype": "Text" + "oldfieldtype": "Text", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", "fieldtype": "Small Text", "label": "Remarks", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1025,19 +1245,25 @@ "fieldname": "transporter_info", "fieldtype": "Section Break", "label": "Transporter Details", - "options": "fa fa-truck" + "options": "fa fa-truck", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "transporter_name", "fieldtype": "Data", "label": "Transporter Name", "oldfieldname": "transporter_name", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break5", "fieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1048,6 +1274,8 @@ "oldfieldname": "lr_no", "oldfieldtype": "Data", "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -1058,6 +1286,8 @@ "oldfieldname": "lr_date", "oldfieldtype": "Date", "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -1066,26 +1296,48 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_reference", "fieldtype": "Link", "label": "Inter Company Reference", "options": "Delivery Note", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-04-18 18:02:18.020763", + "modified": "2020-06-13 22:26:03.600092", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", @@ -1152,4 +1404,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index e6ab8d634d..d0ba001d7e 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -211,6 +211,7 @@ class PurchaseReceipt(BuyingController): stock_rbnb = self.get_company_default("stock_received_but_not_billed") landed_cost_entries = get_item_account_wise_additional_cost(self.name) expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") + auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items')) gl_entries = [] warehouse_with_no_account = [] @@ -301,6 +302,32 @@ class PurchaseReceipt(BuyingController): elif d.warehouse not in warehouse_with_no_account or \ d.rejected_warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(d.warehouse) + elif d.item_code not in stock_items and flt(d.qty) and auto_accounting_for_non_stock_items: + + service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") + credit_currency = get_account_currency(service_received_but_not_billed_account) + + gl_entries.append(self.get_gl_dict({ + "account": service_received_but_not_billed_account, + "against": d.expense_account, + "cost_center": d.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Service"), + "project": d.project, + "credit": d.amount, + "voucher_detail_no": d.name + }, credit_currency, item=d)) + + debit_currency = get_account_currency(d.expense_account) + + gl_entries.append(self.get_gl_dict({ + "account": d.expense_account, + "against": service_received_but_not_billed_account, + "cost_center": d.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Service"), + "project": d.project, + "debit": d.amount, + "voucher_detail_no": d.name + }, debit_currency, item=d)) self.get_asset_gl_entry(gl_entries) # Cost center-wise amount breakup for other charges included for valuation diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 18d68539da..229cf027bd 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -500,7 +500,7 @@ class StockEntry(StockController): if raw_material_cost and self.purpose == "Manufacture": d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) - elif self.purpose == "Repack" and total_fg_qty: + elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually: d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty) d.basic_amount = d.basic_rate * d.qty @@ -574,9 +574,7 @@ class StockEntry(StockController): {"parent": self.purchase_order, "item_code": se_item.subcontracted_item}, "bom") - allow_alternative_item = frappe.get_value("BOM", bom_no, "allow_alternative_item") - - if allow_alternative_item: + if se_item.allow_alternative_item: original_item_code = frappe.get_value("Item Alternative", {"alternative_item_code": item_code}, "item_code") required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \ @@ -743,7 +741,7 @@ class StockEntry(StockController): def get_item_details(self, args=None, for_update=False): item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group, - i.has_batch_no, i.sample_quantity, i.has_serial_no, + i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item, id.expense_account, id.buying_cost_center from `tabItem` i LEFT JOIN `tabItem Default` id ON i.name=id.parent and id.company=%s where i.name=%s @@ -778,6 +776,9 @@ class StockEntry(StockController): 'expense_account' : item.expense_account }) + if self.purpose == 'Send to Subcontractor': + ret["allow_alternative_item"] = item.allow_alternative_item + # update uom if args.get("uom") and for_update: ret.update(get_uom_details(args.get('item_code'), args.get('uom'), args.get('qty'))) diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index c16a41c24f..7b9c129804 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -23,6 +23,7 @@ "image", "image_view", "quantity_and_rate", + "set_basic_rate_manually", "qty", "basic_rate", "basic_amount", @@ -491,12 +492,21 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:parent.purpose===\"Repack\" && doc.t_warehouse", + "fieldname": "set_basic_rate_manually", + "fieldtype": "Check", + "label": "Set Basic Rate Manually", + "show_days": 1, + "show_seconds": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-23 19:19:28.539769", + "modified": "2020-06-08 12:57:03.172887", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 877d0c3bbf..48624e0f25 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -20,7 +20,7 @@ frappe.tour['Stock Settings'] = [ { fieldname: "item_naming_by", title: __("Item Naming By"), - description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set Naming Series choose the 'Naming Series' option.") + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a ") + "Naming Series" + __(" choose the 'Naming Series' option."), }, { fieldname: "default_warehouse", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index c9a3527e91..9c5d3d8340 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -216,7 +216,7 @@ "idx": 1, "issingle": 1, "links": [], - "modified": "2020-04-01 18:11:25.417678", + "modified": "2020-06-20 11:39:15.344112", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json index de24575a14..10f05d4520 100644 --- a/erpnext/stock/module_onboarding/stock/stock.json +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 19:03:23.602423", + "modified": "2020-06-29 16:41:09.440378", "modified_by": "Administrator", "module": "Stock", "name": "Stock", @@ -31,15 +31,15 @@ { "step": "Create a Product" }, + { + "step": "Create a Supplier" + }, { "step": "Introduction to Stock Entry" }, { "step": "Create a Stock Entry" }, - { - "step": "Create a Supplier" - }, { "step": "Create a Purchase Receipt" }, @@ -49,6 +49,5 @@ ], "subtitle": "Inventory, Warehouses, Analysis and more.", "success_message": "The Stock Module is all set up!", - "title": "Let's Setup the Stock Module.", - "user_can_dismiss": 1 + "title": "Let's Set Up the Stock Module." } \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json index 7a64224bd4..4e753f4d84 100644 --- a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 22:09:10.043554", + "modified": "2020-06-29 16:36:53.948242", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index 5a931e7efa..446d3049b7 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -26,9 +26,10 @@ def get_chart_data(data, filters): # consider only consolidated row data = [row for row in data if row[0]] + data = sorted(data, key = lambda i: i[-1],reverse=True) + if len(data) > 10: # get top 10 if data too long - data = sorted(data, key = lambda i: i[-1],reverse=True) data = data[:10] for row in data: diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py index 9a972104a2..5df3fa8067 100644 --- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py +++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py @@ -8,7 +8,7 @@ from frappe.utils import getdate, flt def execute(filters=None): if not filters: filters = {} - float_preceision = frappe.db.get_default("float_preceision") + float_precision = frappe.db.get_default("float_precision") condition = get_condition(filters) @@ -25,7 +25,7 @@ def execute(filters=None): data = [] for item in items: total_outgoing = flt(consumed_item_map.get(item.name, 0)) + flt(delivered_item_map.get(item.name,0)) - avg_daily_outgoing = flt(total_outgoing / diff, float_preceision) + avg_daily_outgoing = flt(total_outgoing / diff, float_precision) reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.safety_stock) data.append([item.name, item.item_name, item.item_group, item.brand, item.description, diff --git a/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json b/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json deleted file mode 100644 index aa5fd0f165..0000000000 --- a/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "add_total_row": 1, - "creation": "2018-01-09 18:38:23.540100", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-04-01 22:10:09.829361", - "modified_by": "Administrator", - "module": "Stock", - "name": "Ordered Items To Be Delivered", - "owner": "Administrator", - "prepared_report": 0, - "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`status` as \"Status:Data:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name::150\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project:Link/Project:120\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.qty as \"Qty:Float:140\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float:140\",\n (`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0)) as \"Qty to Deliver:Float:140\",\n `tabSales Order Item`.base_rate as \"Rate:Float:140\",\n `tabSales Order Item`.base_amount as \"Amount:Float:140\",\n ((`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0))*`tabSales Order Item`.base_rate) as \"Amount to Deliver:Float:140\",\n `tabBin`.actual_qty as \"Available Qty:Float:120\",\n `tabBin`.projected_qty as \"Projected Qty:Float:120\",\n `tabSales Order Item`.`delivery_date` as \"Item Delivery Date:Date:120\",\n DATEDIFF(CURDATE(),`tabSales Order Item`.`delivery_date`) as \"Delay Days:Int:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order Item`.item_group as \"Item Group:Link/Item Group:120\",\n `tabSales Order Item`.warehouse as \"Warehouse:Link/Warehouse:200\"\nfrom\n `tabSales Order` JOIN `tabSales Order Item` \n LEFT JOIN `tabBin` ON (`tabBin`.item_code = `tabSales Order Item`.item_code\n and `tabBin`.warehouse = `tabSales Order Item`.warehouse)\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status not in (\"Stopped\", \"Closed\")\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc", - "ref_doctype": "Delivery Note", - "report_name": "Ordered Items To Be Delivered", - "report_type": "Query Report", - "roles": [ - { - "role": "Stock User" - }, - { - "role": "Stock Manager" - }, - { - "role": "Sales User" - }, - { - "role": "Accounts User" - } - ] -} \ No newline at end of file diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index af99780155..723ed5c1c4 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -180,10 +180,10 @@ def get_fifo_queue(filters, sle=None): qty_to_pop = abs(d.actual_qty) while qty_to_pop: batch = fifo_queue[0] if fifo_queue else [0, None] - if 0 < batch[0] <= qty_to_pop: + if 0 < flt(batch[0]) <= qty_to_pop: # if batch qty > 0 # not enough or exactly same qty in current batch, clear batch - qty_to_pop -= batch[0] + qty_to_pop -= flt(batch[0]) transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0)) else: # all from current batch @@ -262,4 +262,4 @@ def get_chart_data(data, filters): ] }, "type" : "bar" - } \ No newline at end of file + } diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index f21dc3f8b0..11e758fce3 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -230,12 +230,12 @@ def get_valuation_method(item_code): def get_fifo_rate(previous_stock_queue, qty): """get FIFO (average) Rate from Queue""" - if qty >= 0: + if flt(qty) >= 0: total = sum(f[0] for f in previous_stock_queue) return sum(flt(f[0]) * flt(f[1]) for f in previous_stock_queue) / flt(total) if total else 0.0 else: available_qty_for_outgoing, outgoing_cost = 0, 0 - qty_to_pop = abs(qty) + qty_to_pop = abs(flt(qty)) while qty_to_pop and previous_stock_queue: batch = previous_stock_queue[0] if 0 < batch[0] <= qty_to_pop: diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index a3fe72d051..b1ad7c8aa0 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Service Level Agreement", - "links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -43,7 +43,7 @@ "idx": 0, "is_standard": 1, "label": "Support", - "modified": "2020-05-28 13:51:23.869954", + "modified": "2020-06-04 11:54:56.124219", "modified_by": "Administrator", "module": "Support", "name": "Support", @@ -65,8 +65,8 @@ "type": "DocType" }, { - "label": "Service Level", - "link_to": "Service Level", + "label": "Service Level Agreement", + "link_to": "Service Level Agreement", "type": "DocType" } ] diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index bad40cc37f..e7e5bd312b 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -38,10 +38,35 @@ frappe.ui.form.on("Issue", { }, refresh: function (frm) { - if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") { if (frm.doc.service_level_agreement) { - set_time_to_resolve_and_response(frm); + frappe.call({ + 'method': 'frappe.client.get', + args: { + doctype: 'Service Level Agreement', + name: frm.doc.service_level_agreement + }, + callback: function(data) { + let statuses = data.message.pause_sla_on; + const hold_statuses = []; + $.each(statuses, (_i, entry) => { + hold_statuses.push(entry.status); + }); + if (hold_statuses.includes(frm.doc.status)) { + frm.dashboard.clear_headline(); + let message = {"indicator": "orange", "msg": __("SLA is on hold since {0}", [moment(frm.doc.on_hold_since).fromNow(true)])}; + frm.dashboard.set_headline_alert( + '
    ' + + '
    ' + + ''+ message.msg +' ' + + '
    ' + + '
    ' + ); + } else { + set_time_to_resolve_and_response(frm); + } + } + }); } frm.add_custom_button(__("Close"), function () { @@ -55,6 +80,7 @@ frappe.ui.form.on("Issue", { frm: frm }); }, __("Make")); + } else { if (frm.doc.service_level_agreement) { frm.dashboard.clear_headline(); diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index c12cef4a5f..6525ab27d3 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -31,9 +31,13 @@ "resolution_by", "resolution_by_variance", "service_level_agreement_creation", + "on_hold_since", + "total_hold_time", "response", "mins_to_first_response", "first_responded_on", + "column_break_26", + "avg_response_time", "additional_info", "lead", "contact", @@ -50,7 +54,9 @@ "resolution_date", "content_type", "attachment", - "via_customer_portal" + "via_customer_portal", + "resolution_time", + "user_resolution_time" ], "fields": [ { @@ -114,7 +120,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Open\nReplied\nHold\nClosed", + "options": "Open\nReplied\nHold\nResolved\nClosed", "search_index": 1 }, { @@ -161,6 +167,7 @@ "options": "Service Level Agreement" }, { + "depends_on": "eval: doc.status != 'Replied';", "fieldname": "response_by", "fieldtype": "Datetime", "label": "Response By", @@ -174,6 +181,7 @@ "read_only": 1 }, { + "depends_on": "eval: doc.status != 'Replied';", "fieldname": "resolution_by", "fieldtype": "Datetime", "label": "Resolution By", @@ -328,7 +336,7 @@ "read_only": 1 }, { - "depends_on": "eval: doc.service_level_agreement", + "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';", "description": "in hours", "fieldname": "response_by_variance", "fieldtype": "Float", @@ -336,7 +344,7 @@ "read_only": 1 }, { - "depends_on": "eval: doc.service_level_agreement", + "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';", "description": "in hours", "fieldname": "resolution_by_variance", "fieldtype": "Float", @@ -362,12 +370,48 @@ "label": "Issue Split From", "options": "Issue", "read_only": 1 + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "fieldname": "avg_response_time", + "fieldtype": "Duration", + "label": "Average Response Time", + "read_only": 1 + }, + { + "fieldname": "resolution_time", + "fieldtype": "Duration", + "label": "Resolution Time", + "read_only": 1 + }, + { + "fieldname": "user_resolution_time", + "fieldtype": "Duration", + "label": "User Resolution Time", + "read_only": 1 + }, + { + "fieldname": "on_hold_since", + "fieldtype": "Datetime", + "hidden": 1, + "label": "On Hold Since", + "read_only": 1 + }, + { + "fieldname": "total_hold_time", + "fieldtype": "Duration", + "label": "Total Hold Time", + "read_only": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-03-13 02:19:49.477928", + "modified": "2020-06-10 12:47:37.146914", "modified_by": "Administrator", "module": "Support", "name": "Issue", @@ -395,4 +439,4 @@ "title_field": "subject", "track_changes": 1, "track_seen": 1 -} +} \ No newline at end of file diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 117267f1a4..87168e151e 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -7,7 +7,7 @@ import json from frappe import _ from frappe import utils from frappe.model.document import Document -from frappe.utils import now, time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime +from frappe.utils import time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff from datetime import datetime, timedelta from frappe.model.mapper import get_mapped_doc from frappe.utils.user import is_website_user @@ -47,8 +47,8 @@ class Issue(Document): self.contact = frappe.db.get_value("Contact", {"email_id": email_id}) if self.contact: - contact = frappe.get_doc('Contact', self.contact) - self.customer = contact.get_link_for('Customer') + contact = frappe.get_doc("Contact", self.contact) + self.customer = contact.get_link_for("Customer") if not self.company: self.company = frappe.db.get_value("Lead", self.lead, "company") or \ @@ -56,18 +56,75 @@ class Issue(Document): def update_status(self): status = frappe.db.get_value("Issue", self.name, "status") - if self.status!="Open" and status =="Open" and not self.first_responded_on: + if self.status != "Open" and status == "Open" and not self.first_responded_on: self.first_responded_on = frappe.flags.current_time or now_datetime() - if self.status=="Closed" and status !="Closed": + if self.status in ["Closed", "Resolved"] and status not in ["Resolved", "Closed"]: self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) self.update_agreement_status() + set_resolution_time(issue=self) + set_user_resolution_time(issue=self) - if self.status=="Open" and status !="Open": + if self.status == "Open" and status != "Open": # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None + self.reset_issue_metrics() + # enable SLA and variance on Reopen + self.agreement_fulfilled = "Ongoing" + set_service_level_agreement_variance(issue=self.name) + + self.handle_hold_time(status) + + def handle_hold_time(self, status): + if self.service_level_agreement: + # set response and resolution variance as None as the issue is on Hold + pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"], + filters={"parent": self.service_level_agreement}) + hold_statuses = [entry.status for entry in pause_sla_on] + update_values = {} + + if hold_statuses: + if self.status in hold_statuses and status not in hold_statuses: + update_values['on_hold_since'] = frappe.flags.current_time or now_datetime() + if not self.first_responded_on: + update_values['response_by'] = None + update_values['response_by_variance'] = 0 + update_values['resolution_by'] = None + update_values['resolution_by_variance'] = 0 + + # calculate hold time when status is changed from any hold status to any non-hold status + if self.status not in hold_statuses and status in hold_statuses: + hold_time = self.total_hold_time if self.total_hold_time else 0 + now_time = frappe.flags.current_time or now_datetime() + last_hold_time = 0 + if self.on_hold_since: + # last_hold_time will be added to the sla variables + last_hold_time = time_diff_in_seconds(now_time, self.on_hold_since) + update_values['total_hold_time'] = hold_time + last_hold_time + + # re-calculate SLA variables after issue changes from any hold status to any non-hold status + # add hold time to SLA variables + start_date_time = get_datetime(self.service_level_agreement_creation) + priority = get_priority(self) + now_time = frappe.flags.current_time or now_datetime() + + if not self.first_responded_on: + response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) + response_by = add_to_date(response_by, seconds=round(last_hold_time)) + response_by_variance = round(time_diff_in_hours(response_by, now_time)) + update_values['response_by'] = response_by + update_values['response_by_variance'] = response_by_variance + (last_hold_time // 3600) + + resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) + resolution_by = add_to_date(resolution_by, seconds=round(last_hold_time)) + resolution_by_variance = round(time_diff_in_hours(resolution_by, now_time)) + update_values['resolution_by'] = resolution_by + update_values['resolution_by_variance'] = resolution_by_variance + (last_hold_time // 3600) + update_values['on_hold_since'] = None + + self.db_set(update_values) def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": @@ -128,6 +185,7 @@ class Issue(Document): replicated_issue.response_by_variance = None replicated_issue.resolution_by = None replicated_issue.resolution_by_variance = None + replicated_issue.reset_issue_metrics() frappe.get_doc(replicated_issue).insert() @@ -137,7 +195,7 @@ class Issue(Document): communications = frappe.get_all("Communication", filters={"reference_doctype": "Issue", "reference_name": comm_to_split_from.reference_name, - "creation": ('>=', comm_to_split_from.creation)}) + "creation": (">=", comm_to_split_from.creation)}) for communication in communications: doc = frappe.get_doc("Communication", communication.name) @@ -173,20 +231,15 @@ class Issue(Document): self.service_level_agreement = service_level_agreement.name self.priority = service_level_agreement.default_priority if not priority else priority - service_level_agreement = frappe.get_doc("Service Level Agreement", service_level_agreement.name) - priority = service_level_agreement.get_service_level_agreement_priority(self.priority) - priority.update({ - "support_and_resolution": service_level_agreement.support_and_resolution, - "holiday_list": service_level_agreement.holiday_list - }) + priority = get_priority(self) if not self.creation: self.creation = now_datetime() self.service_level_agreement_creation = now_datetime() start_date_time = get_datetime(self.service_level_agreement_creation) - self.response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) - self.resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) + self.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) + self.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) self.response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) @@ -221,36 +274,41 @@ class Issue(Document): self.agreement_fulfilled = "Ongoing" self.save() + def reset_issue_metrics(self): + self.db_set("resolution_time", None) + self.db_set("user_resolution_time", None) + + +def get_priority(issue): + service_level_agreement = frappe.get_doc("Service Level Agreement", issue.service_level_agreement) + priority = service_level_agreement.get_service_level_agreement_priority(issue.priority) + priority.update({ + "support_and_resolution": service_level_agreement.support_and_resolution, + "holiday_list": service_level_agreement.holiday_list + }) + return priority + + def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time expected_time = current_date_time start_time = None end_time = None - # lets assume response time is in days by default - if parameter == 'response': - allotted_days = service_level.get("response_time") - time_period = service_level.get("response_time_period") - elif parameter == 'resolution': - allotted_days = service_level.get("resolution_time") - time_period = service_level.get("resolution_time_period") + if parameter == "response": + allotted_seconds = service_level.get("response_time") + elif parameter == "resolution": + allotted_seconds = service_level.get("resolution_time") else: frappe.throw(_("{0} parameter is invalid").format(parameter)) - allotted_hours = 0 - if time_period == 'Hour': - allotted_hours = allotted_days - allotted_days = 0 - elif time_period == 'Week': - allotted_days *= 7 - - expected_time_is_set = 1 if allotted_days == 0 and time_period in ['Day', 'Week'] else 0 + expected_time_is_set = 0 support_days = {} for service in service_level.get("support_and_resolution"): support_days[service.workday] = frappe._dict({ - 'start_time': service.start_time, - 'end_time': service.end_time, + "start_time": service.start_time, + "end_time": service.end_time, }) holidays = get_holidays(service_level.get("holiday_list")) @@ -264,25 +322,22 @@ def get_expected_time_for(parameter, service_level, start_date_time): if getdate(current_date_time) == getdate(start_date_time) and get_time_in_timedelta(current_date_time.time()) > support_days[current_weekday].start_time \ else support_days[current_weekday].start_time end_time = support_days[current_weekday].end_time - time_left_today = time_diff_in_hours(end_time, start_time) + time_left_today = time_diff_in_seconds(end_time, start_time) # no time left for support today - if time_left_today < 0: pass - elif time_period == 'Hour': - if time_left_today >= allotted_hours: + if time_left_today <= 0: pass + elif allotted_seconds: + if time_left_today >= allotted_seconds: expected_time = datetime.combine(getdate(current_date_time), get_time(start_time)) - expected_time = add_to_date(expected_time, hours=allotted_hours) + expected_time = add_to_date(expected_time, seconds=allotted_seconds) expected_time_is_set = 1 else: - allotted_hours = allotted_hours - time_left_today - else: - allotted_days -= 1 - expected_time_is_set = allotted_days <= 0 + allotted_seconds = allotted_seconds - time_left_today if not expected_time_is_set: current_date_time = add_to_date(current_date_time, days=1) - if end_time and time_period != 'Hour': + if end_time and allotted_seconds >= 86400: current_date_time = datetime.combine(getdate(current_date_time), get_time(end_time)) else: current_date_time = expected_time @@ -311,6 +366,36 @@ def set_service_level_agreement_variance(issue=None): if variance < 0: frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) + +def set_resolution_time(issue): + # total time taken from issue creation to closing + resolution_time = time_diff_in_seconds(issue.resolution_date, issue.creation) + issue.db_set("resolution_time", resolution_time) + + +def set_user_resolution_time(issue): + # total time taken by a user to close the issue apart from wait_time + communications = frappe.get_list("Communication", filters={ + "reference_doctype": issue.doctype, + "reference_name": issue.name + }, + fields=["sent_or_received", "name", "creation"], + order_by="creation" + ) + + pending_time = [] + for i in range(len(communications)): + if communications[i].sent_or_received == "Received" and communications[i-1].sent_or_received == "Sent": + wait_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) + if wait_time > 0: + pending_time.append(wait_time) + + total_pending_time = sum(pending_time) + resolution_time_in_secs = time_diff_in_seconds(issue.resolution_date, issue.creation) + user_resolution_time = resolution_time_in_secs - total_pending_time + issue.db_set("user_resolution_time", user_resolution_time) + + def get_list_context(context=None): return { "title": _("Issues"), @@ -318,7 +403,7 @@ def get_list_context(context=None): "row_template": "templates/includes/issue_row.html", "show_sidebar": True, "show_search": True, - 'no_breadcrumbs': True + "no_breadcrumbs": True } @@ -326,12 +411,12 @@ def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, ord from frappe.www.list import get_list user = frappe.session.user - contact = frappe.db.get_value('Contact', {'user': user}, 'name') + contact = frappe.db.get_value("Contact", {"user": user}, "name") customer = None if contact: - contact_doc = frappe.get_doc('Contact', contact) - customer = contact_doc.get_link_for('Customer') + contact_doc = frappe.get_doc("Contact", contact) + customer = contact_doc.get_link_for("Customer") ignore_permissions = False if is_website_user(): diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 7a5e3e300d..fb8ceb53b2 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -5,15 +5,18 @@ from __future__ import unicode_literals import frappe import unittest from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues -from frappe.utils import now_datetime, get_datetime +from frappe.utils import now_datetime, get_datetime, flt import datetime from datetime import timedelta class TestIssue(unittest.TestCase): - def test_response_time_and_resolution_time_based_on_different_sla(self): + def setUp(self): + frappe.db.sql("delete from `tabService Level Agreement`") + frappe.db.sql("delete from `tabEmployee`") frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) create_service_level_agreements_for_issues() + def test_response_time_and_resolution_time_based_on_different_sla(self): creation = datetime.datetime(2019, 3, 4, 12, 0) # make issue with customer specific SLA @@ -72,8 +75,67 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.agreement_fulfilled, 'Fulfilled') -def make_issue(creation=None, customer=None, index=0): + def test_issue_metrics(self): + creation = datetime.datetime(2020, 3, 4, 4, 0) + issue = make_issue(creation, index=1) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 4, 15) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + creation = datetime.datetime(2020, 3, 4, 5, 0) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 5, 5) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5) + issue.reload() + issue.status = 'Closed' + issue.save() + + self.assertEqual(issue.avg_response_time, 600) + self.assertEqual(issue.resolution_time, 3900) + self.assertEqual(issue.user_resolution_time, 1200) + + def test_hold_time_on_replied(self): + creation = datetime.datetime(2020, 3, 4, 4, 0) + + issue = make_issue(creation, index=1) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 4, 15) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 4, 15) + issue.reload() + issue.status = 'Replied' + issue.save() + + self.assertEqual(issue.on_hold_since, frappe.flags.current_time) + + creation = datetime.datetime(2020, 3, 4, 5, 0) + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 0) + create_communication(issue.name, "test@example.com", "Received", creation) + + issue.reload() + self.assertEqual(flt(issue.total_hold_time, 2), 2700) + self.assertEqual(issue.resolution_by, datetime.datetime(2020, 3, 4, 16, 45)) + + creation = datetime.datetime(2020, 3, 4, 5, 5) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5) + issue.reload() + issue.status = 'Closed' + issue.save() + + issue.reload() + self.assertEqual(flt(issue.total_hold_time, 2), 2700) + + +def make_issue(creation=None, customer=None, index=0): issue = frappe.get_doc({ "doctype": "Issue", "subject": "Service Level Agreement Issue {0}".format(index), @@ -86,6 +148,7 @@ def make_issue(creation=None, customer=None, index=0): return issue + def create_customer(name, customer_group, territory): create_customer_group(customer_group) @@ -99,6 +162,7 @@ def create_customer(name, customer_group, territory): "territory": territory }).insert(ignore_permissions=True) + def create_customer_group(customer_group): if not frappe.db.exists("Customer Group", {"customer_group_name": customer_group}): @@ -107,6 +171,7 @@ def create_customer_group(customer_group): "customer_group_name": customer_group }).insert(ignore_permissions=True) + def create_territory(territory): if not frappe.db.exists("Territory", {"territory_name": territory}): @@ -114,3 +179,21 @@ def create_territory(territory): "doctype": "Territory", "territory_name": territory, }).insert(ignore_permissions=True) + + +def create_communication(reference_name, sender, sent_or_received, creation): + issue = frappe.get_doc({ + "doctype": "Communication", + "communication_type": "Communication", + "communication_medium": "Email", + "sent_or_received": sent_or_received, + "email_status": "Open", + "subject": "Test Issue", + "sender": sender, + "content": "Test", + "status": "Linked", + "reference_doctype": "Issue", + "creation": creation, + "reference_name": reference_name + }) + issue.save() diff --git a/erpnext/support/doctype/pause_sla_on_status/__init__.py b/erpnext/support/doctype/pause_sla_on_status/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json new file mode 100644 index 0000000000..5b03f25f48 --- /dev/null +++ b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "creation": "2020-06-05 13:59:43.265588", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "status" + ], + "fields": [ + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-05 15:15:29.986608", + "modified_by": "Administrator", + "module": "Support", + "name": "Pause SLA On Status", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py new file mode 100644 index 0000000000..a3b547e480 --- /dev/null +++ b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PauseSLAOnStatus(Document): + pass diff --git a/erpnext/support/doctype/service_level/service_level.js b/erpnext/support/doctype/service_level/service_level.js deleted file mode 100644 index abe254bd03..0000000000 --- a/erpnext/support/doctype/service_level/service_level.js +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Service Level', { - -}); diff --git a/erpnext/support/doctype/service_level/service_level.json b/erpnext/support/doctype/service_level/service_level.json deleted file mode 100644 index dced3aa9e9..0000000000 --- a/erpnext/support/doctype/service_level/service_level.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "autoname": "field:service_level", - "creation": "2018-11-19 12:44:30.407502", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "service_level", - "employee_group", - "column_break_2", - "holiday_list", - "default_priority", - "response_and_resoution_time", - "priorities", - "section_break_01", - "support_and_resolution" - ], - "fields": [ - { - "fieldname": "service_level", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Level", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "holiday_list", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Holiday List (ignored during SLA calculation)", - "options": "Holiday List", - "reqd": 1 - }, - { - "fieldname": "employee_group", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee Group", - "options": "Employee Group" - }, - { - "fieldname": "response_and_resoution_time", - "fieldtype": "Section Break", - "label": "Response and Resoution Time" - }, - { - "fieldname": "section_break_01", - "fieldtype": "Section Break", - "label": "Support Hours" - }, - { - "fieldname": "support_and_resolution", - "fieldtype": "Table", - "label": "Support and Resolution", - "options": "Service Day", - "reqd": 1 - }, - { - "fieldname": "priorities", - "fieldtype": "Table", - "label": "Priorities", - "options": "Service Level Priority", - "reqd": 1 - }, - { - "fieldname": "default_priority", - "fieldtype": "Link", - "label": "Default Priority", - "options": "Issue Priority", - "read_only": 1 - } - ], - "modified": "2019-06-06 12:58:03.464056", - "modified_by": "Administrator", - "module": "Support", - "name": "Service Level", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "All", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/support/doctype/service_level/service_level.py b/erpnext/support/doctype/service_level/service_level.py deleted file mode 100644 index 89fa25c233..0000000000 --- a/erpnext/support/doctype/service_level/service_level.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.model.document import Document -from datetime import datetime -from frappe.utils import get_weekdays - -class ServiceLevel(Document): - - def validate(self): - self.check_priorities() - self.check_support_and_resolution() - - def check_priorities(self): - default_priority = [] - priorities = [] - - for priority in self.priorities: - # Check if response and resolution time is set for every priority - if not (priority.response_time or priority.resolution_time): - frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.").format(priority.priority, priority.idx)) - - priorities.append(priority.priority) - - if priority.default_priority: - default_priority.append(priority.default_priority) - - if priority.response_time_period == "Hour": - response = priority.response_time * 0.0416667 - elif priority.response_time_period == "Day": - response = priority.response_time - elif priority.response_time_period == "Week": - response = priority.response_time * 7 - - if priority.resolution_time_period == "Hour": - resolution = priority.resolution_time * 0.0416667 - elif priority.resolution_time_period == "Day": - resolution = priority.resolution_time - elif priority.resolution_time_period == "Week": - resolution = priority.resolution_time * 7 - - if response > resolution: - frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) - - # Check if repeated priority - if not len(set(priorities)) == len(priorities): - repeated_priority = get_repeated(priorities) - frappe.throw(_("Priority {0} has been repeated.").format(repeated_priority)) - - # Check if repeated default priority - if not len(set(default_priority)) == len(default_priority): - frappe.throw(_("Select only one Priority as Default.")) - - # set default priority from priorities - try: - self.default_priority = next(d.priority for d in self.priorities if d.default_priority) - except Exception: - frappe.throw(_("Select a Default Priority.")) - - def check_support_and_resolution(self): - week = get_weekdays() - support_days = [] - - for support_and_resolution in self.support_and_resolution: - # Check if start and end time is set for every support day - if not (support_and_resolution.start_time or support_and_resolution.end_time): - frappe.throw(_("Set Start Time and End Time for \ - Support Day {0} at index {1}.".format(support_and_resolution.workday, support_and_resolution.idx))) - - support_days.append(support_and_resolution.workday) - support_and_resolution.idx = week.index(support_and_resolution.workday) + 1 - - if support_and_resolution.start_time >= support_and_resolution.end_time: - frappe.throw(_("Start Time can't be greater than or equal to End Time \ - for {0}.".format(support_and_resolution.workday))) - - # Check for repeated workday - if not len(set(support_days)) == len(support_days): - repeated_days = get_repeated(support_days) - frappe.throw(_("Workday {0} has been repeated.").format(repeated_days)) - -def get_repeated(values): - unique_list = [] - diff = [] - for value in values: - if value not in unique_list: - unique_list.append(str(value)) - else: - if value not in diff: - diff.append(str(value)) - return " ".join(diff) diff --git a/erpnext/support/doctype/service_level/service_level_dashboard.py b/erpnext/support/doctype/service_level/service_level_dashboard.py deleted file mode 100644 index 393095e117..0000000000 --- a/erpnext/support/doctype/service_level/service_level_dashboard.py +++ /dev/null @@ -1,12 +0,0 @@ -from frappe import _ - -def get_data(): - return { - 'fieldname': 'service_level', - 'transactions': [ - { - 'label': _('Service Level Agreement'), - 'items': ['Service Level Agreement'] - } - ] - } \ No newline at end of file diff --git a/erpnext/support/doctype/service_level/test_service_level.py b/erpnext/support/doctype/service_level/test_service_level.py deleted file mode 100644 index 09577df166..0000000000 --- a/erpnext/support/doctype/service_level/test_service_level.py +++ /dev/null @@ -1,149 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals -from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_group -from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities - -import frappe -import unittest - -class TestServiceLevel(unittest.TestCase): - - def test_service_level(self): - employee_group = make_employee_group() - make_holiday_list() - make_priorities() - - # Default Service Level - test_make_service_level = create_service_level("__Test Service Level", "__Test Holiday List", employee_group, 4, 6) - get_make_service_level = get_service_level("__Test Service Level") - - self.assertEqual(test_make_service_level.name, get_make_service_level.name) - self.assertEqual(test_make_service_level.holiday_list, get_make_service_level.holiday_list) - self.assertEqual(test_make_service_level.employee_group, get_make_service_level.employee_group) - - # Service Level - test_make_service_level = create_service_level("_Test Service Level", "__Test Holiday List", employee_group, 2, 3) - get_make_service_level = get_service_level("_Test Service Level") - - self.assertEqual(test_make_service_level.name, get_make_service_level.name) - self.assertEqual(test_make_service_level.holiday_list, get_make_service_level.holiday_list) - self.assertEqual(test_make_service_level.employee_group, get_make_service_level.employee_group) - - -def create_service_level(service_level, holiday_list, employee_group, response_time, resolution_time): - sl = frappe.get_doc({ - "doctype": "Service Level", - "service_level": service_level, - "holiday_list": holiday_list, - "employee_group": employee_group, - "priorities": [ - { - "priority": "Low", - "response_time": response_time, - "response_time_period": "Hour", - "resolution_time": resolution_time, - "resolution_time_period": "Hour", - }, - { - "priority": "Medium", - "response_time": response_time, - "default_priority": 1, - "response_time_period": "Hour", - "resolution_time": resolution_time, - "resolution_time_period": "Hour", - }, - { - "priority": "High", - "response_time": response_time, - "response_time_period": "Hour", - "resolution_time": resolution_time, - "resolution_time_period": "Hour", - } - ], - "support_and_resolution": [ - { - "workday": "Monday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Tuesday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Wednesday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Thursday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Friday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Saturday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Sunday", - "start_time": "10:00:00", - "end_time": "18:00:00", - } - ] - }) - - sl_exists = frappe.db.exists("Service Level", {"service_level": service_level}) - - if not sl_exists: - sl.insert() - return sl - else: - return frappe.get_doc("Service Level", {"service_level": service_level}) - -def get_service_level(service_level): - return frappe.get_doc("Service Level", service_level) - -def make_holiday_list(): - holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") - if not holiday_list: - now = frappe.utils.now_datetime() - holiday_list = frappe.get_doc({ - "doctype": "Holiday List", - "holiday_list_name": "__Test Holiday List", - "from_date": "2019-01-01", - "to_date": "2019-12-31", - "holidays": [ - { - "description": "Test Holiday 1", - "holiday_date": "2019-03-05" - }, - { - "description": "Test Holiday 2", - "holiday_date": "2019-03-07" - }, - { - "description": "Test Holiday 3", - "holiday_date": "2019-02-11" - }, - ] - }).insert() - -def create_service_level_for_sla(): - employee_group = make_employee_group() - make_holiday_list() - make_priorities() - - # Default Service Level - create_service_level("__Test Service Level", "__Test Holiday List", employee_group, 4, 6) - - # Service Level - create_service_level("_Test Service Level", "__Test Holiday List", employee_group, 2, 3) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js index 1d486f4834..5346195a39 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js @@ -2,28 +2,15 @@ // For license information, please see license.txt frappe.ui.form.on('Service Level Agreement', { - service_level: function(frm) { - frm.fields_dict.support_and_resolution.grid.remove_all(); - frappe.call({ - "method": "frappe.client.get", - args: { - doctype: "Service Level", - name: frm.doc.service_level - }, - callback: function(data){ - let count = Math.max(data.message.priorities.length, data.message.support_and_resolution.length); - let i = 0; - while (i < count){ - if (data.message.priorities[i]) { - frm.add_child("priorities", data.message.priorities[i]); - } - if (data.message.support_and_resolution[i]) { - frm.add_child("support_and_resolution", data.message.support_and_resolution[i]); - } - i++; - } - frm.refresh(); - } + setup: function(frm) { + let allow_statuses = []; + const exclude_statuses = ['Open', 'Closed', 'Resolved']; + + frappe.model.with_doctype('Issue', () => { + let statuses = frappe.meta.get_docfield('Issue', 'status', frm.doc.name).options; + statuses = statuses.split('\n'); + allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status)); + frappe.meta.get_docfield('Pause SLA On Status', 'status', frm.doc.name).options = [''].concat(allow_statuses); }); - }, -}); + } +}); \ No newline at end of file diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 9a83ca7ac0..939c199982 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "format:SLA-{service_level}-{####}", "creation": "2018-12-26 21:08:15.448812", "doctype": "DocType", @@ -6,12 +7,13 @@ "engine": "InnoDB", "field_order": [ "enable", + "section_break_2", "service_level", + "default_priority", "default_service_level_agreement", - "holiday_list", "column_break_2", "employee_group", - "default_priority", + "holiday_list", "entity_section", "entity_type", "column_break_10", @@ -21,49 +23,40 @@ "active", "column_break_7", "end_date", + "section_break_18", + "pause_sla_on", "response_and_resolution_time_section", "priorities", "support_and_resolution_section_break", "support_and_resolution" ], "fields": [ - { - "default": "0", - "depends_on": "eval: !doc.customer;", - "fieldname": "default_service_level_agreement", - "fieldtype": "Check", - "label": "Default Service Level Agreement" - }, { "fieldname": "service_level", - "fieldtype": "Link", + "fieldtype": "Data", "in_list_view": 1, "in_standard_filter": 1, "label": "Service Level", - "options": "Service Level", "reqd": 1 }, { - "fetch_from": "service_level.holiday_list", "fieldname": "holiday_list", "fieldtype": "Link", "label": "Holiday List", "options": "Holiday List", - "read_only": 1 + "reqd": 1 }, { "fieldname": "column_break_2", "fieldtype": "Column Break" }, { - "fetch_from": "service_level.employee_group", "fieldname": "employee_group", "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, "label": "Employee Group", - "options": "Employee Group", - "read_only": 1 + "options": "Employee Group" }, { "fieldname": "agreement_details_section", @@ -103,21 +96,15 @@ "fieldname": "support_and_resolution", "fieldtype": "Table", "label": "Support and Resolution", - "options": "Service Day" + "options": "Service Day", + "reqd": 1 }, { "fieldname": "priorities", "fieldtype": "Table", "label": "Priorities", - "options": "Service Level Priority" - }, - { - "fetch_from": "service_level.default_priority", - "fieldname": "default_priority", - "fieldtype": "Link", - "label": "Default Priority", - "options": "Issue Priority", - "read_only": 1 + "options": "Service Level Priority", + "reqd": 1 }, { "default": "1", @@ -156,9 +143,38 @@ "fieldname": "enable", "fieldtype": "Check", "label": "Enable" + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "default_service_level_agreement", + "fieldtype": "Check", + "label": "Default Service Level Agreement" + }, + { + "fieldname": "default_priority", + "fieldtype": "Link", + "label": "Default Priority", + "options": "Issue Priority", + "read_only": 1 + }, + { + "fieldname": "section_break_18", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "fieldname": "pause_sla_on", + "fieldtype": "Table", + "label": "Pause SLA On", + "options": "Pause SLA On Status" } ], - "modified": "2019-07-09 17:22:16.402939", + "links": [], + "modified": "2020-06-10 12:30:15.050785", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index a399c58b16..c692315706 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -6,11 +6,73 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from frappe.utils import getdate +from frappe.utils import getdate, get_weekdays class ServiceLevelAgreement(Document): def validate(self): + self.validate_doc() + self.check_priorities() + self.check_support_and_resolution() + + def check_priorities(self): + default_priority = [] + priorities = [] + + for priority in self.priorities: + # Check if response and resolution time is set for every priority + if not (priority.response_time or priority.resolution_time): + frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.").format(priority.priority, priority.idx)) + + priorities.append(priority.priority) + + if priority.default_priority: + default_priority.append(priority.default_priority) + + response = priority.response_time + resolution = priority.resolution_time + + if response > resolution: + frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) + + # Check if repeated priority + if not len(set(priorities)) == len(priorities): + repeated_priority = get_repeated(priorities) + frappe.throw(_("Priority {0} has been repeated.").format(repeated_priority)) + + # Check if repeated default priority + if not len(set(default_priority)) == len(default_priority): + frappe.throw(_("Select only one Priority as Default.")) + + # set default priority from priorities + try: + self.default_priority = next(d.priority for d in self.priorities if d.default_priority) + except Exception: + frappe.throw(_("Select a Default Priority.")) + + def check_support_and_resolution(self): + week = get_weekdays() + support_days = [] + + for support_and_resolution in self.support_and_resolution: + # Check if start and end time is set for every support day + if not (support_and_resolution.start_time or support_and_resolution.end_time): + frappe.throw(_("Set Start Time and End Time for \ + Support Day {0} at index {1}.".format(support_and_resolution.workday, support_and_resolution.idx))) + + support_days.append(support_and_resolution.workday) + support_and_resolution.idx = week.index(support_and_resolution.workday) + 1 + + if support_and_resolution.start_time >= support_and_resolution.end_time: + frappe.throw(_("Start Time can't be greater than or equal to End Time \ + for {0}.".format(support_and_resolution.workday))) + + # Check for repeated workday + if not len(set(support_days)) == len(support_days): + repeated_days = get_repeated(support_days) + frappe.throw(_("Workday {0} has been repeated.").format(repeated_days)) + + def validate_doc(self): if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): frappe.throw(_("Service Level Agreement tracking is not enabled.")) @@ -35,9 +97,7 @@ class ServiceLevelAgreement(Document): return frappe._dict({ "priority": priority.priority, "response_time": priority.response_time, - "response_time_period": priority.response_time_period, - "resolution_time": priority.resolution_time, - "resolution_time_period": priority.resolution_time_period + "resolution_time": priority.resolution_time }) def check_agreement_status(): @@ -110,4 +170,15 @@ def get_service_level_agreement_filters(name, customer=None): return { "priority": [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])], "service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters)] - } \ No newline at end of file + } + +def get_repeated(values): + unique_list = [] + diff = [] + for value in values: + if value not in unique_list: + unique_list.append(str(value)) + else: + if value not in diff: + diff.append(str(value)) + return " ".join(diff) diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 4a741ea4e1..07ef368cbe 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -5,19 +5,20 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.support.doctype.service_level.test_service_level import create_service_level_for_sla +from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_group +from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities class TestServiceLevelAgreement(unittest.TestCase): - - def test_service_level_agreement(self): + def setUp(self): + frappe.db.sql("delete from `tabService Level Agreement`") frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) - create_service_level_for_sla() - + def test_service_level_agreement(self): # Default Service Level Agreement create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1, - service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type=None, entity=None, response_time=4, resolution_time=6) + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + entity_type=None, entity=None, response_time=14400, resolution_time=21600) + get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1) self.assertEqual(create_default_service_level_agreement.name, get_default_service_level_agreement.name) @@ -28,8 +29,8 @@ class TestServiceLevelAgreement(unittest.TestCase): # Service Level Agreement for Customer customer = create_customer() create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer", entity=customer, response_time=2, resolution_time=3) + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + entity_type="Customer", entity=customer, response_time=7200, resolution_time=10800) get_customer_service_level_agreement = get_service_level_agreement(entity_type="Customer", entity=customer) self.assertEqual(create_customer_service_level_agreement.name, get_customer_service_level_agreement.name) @@ -40,8 +41,8 @@ class TestServiceLevelAgreement(unittest.TestCase): # Service Level Agreement for Customer Group customer_group = create_customer_group() create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer Group", entity=customer_group, response_time=2, resolution_time=3) + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + entity_type="Customer Group", entity=customer_group, response_time=7200, resolution_time=10800) get_customer_group_service_level_agreement = get_service_level_agreement(entity_type="Customer Group", entity=customer_group) self.assertEqual(create_customer_group_service_level_agreement.name, get_customer_group_service_level_agreement.name) @@ -52,8 +53,8 @@ class TestServiceLevelAgreement(unittest.TestCase): # Service Level Agreement for Territory territory = create_territory() create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Territory", entity=territory, response_time=2, resolution_time=3) + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + entity_type="Territory", entity=territory, response_time=7200, resolution_time=10800) get_territory_service_level_agreement = get_service_level_agreement(entity_type="Territory", entity=territory) self.assertEqual(create_territory_service_level_agreement.name, get_territory_service_level_agreement.name) @@ -71,14 +72,19 @@ def get_service_level_agreement(default_service_level_agreement=None, entity_typ service_level_agreement = frappe.get_doc("Service Level Agreement", filters) return service_level_agreement -def create_service_level_agreement(default_service_level_agreement, service_level, holiday_list, employee_group, +def create_service_level_agreement(default_service_level_agreement, holiday_list, employee_group, response_time, entity_type, entity, resolution_time): + employee_group = make_employee_group() + make_holiday_list() + make_priorities() + service_level_agreement = frappe.get_doc({ "doctype": "Service Level Agreement", "enable": 1, + "service_level": "__Test Service Level", "default_service_level_agreement": default_service_level_agreement, - "service_level": service_level, + "default_priority": "Medium", "holiday_list": holiday_list, "employee_group": employee_group, "entity_type": entity_type, @@ -109,6 +115,11 @@ def create_service_level_agreement(default_service_level_agreement, service_leve "resolution_time_period": "Hour", } ], + "pause_sla_on": [ + { + "status": "Replied" + } + ], "support_and_resolution": [ { "workday": "Monday", @@ -167,6 +178,7 @@ def create_service_level_agreement(default_service_level_agreement, service_leve else: return frappe.get_doc("Service Level Agreement", service_level_agreement_exists) + def create_customer(): customer = frappe.get_doc({ "doctype": "Customer", @@ -206,23 +218,41 @@ def create_territory(): return frappe.db.exists("Territory", {"territory_name": "_Test SLA Territory"}) def create_service_level_agreements_for_issues(): - create_service_level_for_sla() - - create_service_level_agreement(default_service_level_agreement=1, - service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type=None, entity=None, response_time=4, resolution_time=6) + create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=14400, resolution_time=21600) create_customer() - create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer", entity="_Test Customer", response_time=2, resolution_time=3) + create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=7200, resolution_time=10800) create_customer_group() - create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=2, resolution_time=3) + create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=7200, resolution_time=10800) create_territory() - create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Territory", entity="_Test SLA Territory", response_time=2, resolution_time=3) + create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=7200, resolution_time=10800) + +def make_holiday_list(): + holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") + if not holiday_list: + holiday_list = frappe.get_doc({ + "doctype": "Holiday List", + "holiday_list_name": "__Test Holiday List", + "from_date": "2019-01-01", + "to_date": "2019-12-31", + "holidays": [ + { + "description": "Test Holiday 1", + "holiday_date": "2019-03-05" + }, + { + "description": "Test Holiday 2", + "holiday_date": "2019-03-07" + }, + { + "description": "Test Holiday 3", + "holiday_date": "2019-02-11" + }, + ] + }).insert() diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index cd87a1c113..65d51694cc 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-05-04 05:54:03.658991", "doctype": "DocType", "editable_grid": 1, @@ -9,10 +10,8 @@ "default_priority", "sb_00", "response_time", - "response_time_period", "cb_00", - "resolution_time", - "resolution_time_period" + "resolution_time" ], "fields": [ { @@ -28,16 +27,11 @@ "fieldtype": "Section Break" }, { - "columns": 1, - "fieldname": "response_time", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Response Time" - }, - { - "columns": 1, + "columns": 2, "fieldname": "resolution_time", - "fieldtype": "Int", + "fieldtype": "Duration", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "Resolution Time" }, @@ -45,36 +39,31 @@ "fieldname": "cb_00", "fieldtype": "Column Break" }, - { - "columns": 2, - "fieldname": "response_time_period", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Response Time Period", - "options": "Hour\nDay\nWeek" - }, - { - "columns": 2, - "fieldname": "resolution_time_period", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Resolution Time Period", - "options": "Hour\nDay\nWeek" - }, { "fieldname": "cb_01", "fieldtype": "Column Break" }, { + "columns": 1, "default": "0", "fieldname": "default_priority", "fieldtype": "Check", "in_list_view": 1, "label": "Default Priority" + }, + { + "columns": 2, + "fieldname": "response_time", + "fieldtype": "Duration", + "hide_days": 1, + "hide_seconds": 1, + "in_list_view": 1, + "label": "First Response Time" } ], "istable": 1, - "modified": "2019-05-21 06:54:42.674377", + "links": [], + "modified": "2020-06-10 12:45:47.545915", "modified_by": "Administrator", "module": "Support", "name": "Service Level Priority", @@ -84,4 +73,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/support/doctype/support_settings/support_settings.js b/erpnext/support/doctype/support_settings/support_settings.js index 1d1069d58b..78adca81ca 100644 --- a/erpnext/support/doctype/support_settings/support_settings.js +++ b/erpnext/support/doctype/support_settings/support_settings.js @@ -3,6 +3,6 @@ frappe.ui.form.on('Support Settings', { refresh: function(frm) { - + // } }); diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index be9e064591..5d3d3ace59 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -1,4 +1,5 @@ { + "actions": "", "creation": "2017-02-17 13:07:35.686409", "doctype": "DocType", "editable_grid": 1, @@ -21,6 +22,10 @@ "post_description_key", "post_route_key", "post_route_string", + "greetings_section_section", + "greeting_title", + "column_break_19", + "greeting_subtitle", "search_apis_sb", "search_apis" ], @@ -122,13 +127,44 @@ }, { "default": "0", + "depends_on": "eval:doc.track_service_level_agreement;", "fieldname": "allow_resetting_service_level_agreement", "fieldtype": "Check", "label": "Allow Resetting Service Level Agreement" + }, + { + "default": "We're here to help", + "fieldname": "greeting_title", + "fieldtype": "Data", + "label": "Greeting Title", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_19", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "Browse help topics", + "fieldname": "greeting_subtitle", + "fieldtype": "Data", + "label": "Greeting Subtitle", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "greetings_section_section", + "fieldtype": "Section Break", + "label": "Greetings Section", + "show_days": 1, + "show_seconds": 1 } ], "issingle": 1, - "modified": "2019-07-10 22:52:39.663873", + "links": [], + "modified": "2020-06-11 13:08:38.473616", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js index 5fd901169f..163c955c56 100644 --- a/erpnext/templates/generators/item/item_configure.js +++ b/erpnext/templates/generators/item/item_configure.js @@ -193,14 +193,17 @@ class ItemConfigure { filtered_items_count === 1 ? filtered_items[0] : ''; + // Allow Add to Cart if adding out of stock items enabled in Shopping Cart else check stock. + const in_stock = product_info.allow_items_not_in_stock ? 1 : product_info.in_stock; + const add_to_cart = `${__('Add to cart')}`; + const product_action = in_stock ? add_to_cart : `${__('Not in Stock')}`; + const item_add_to_cart = one_item ? ` `: ''; diff --git a/erpnext/templates/generators/student_admission.html b/erpnext/templates/generators/student_admission.html index ae70df8b08..8b153448ee 100644 --- a/erpnext/templates/generators/student_admission.html +++ b/erpnext/templates/generators/student_admission.html @@ -14,12 +14,12 @@ {%- if introduction -%}
    {{ introduction }}
    -{% endif %} +{% endif %} -{%- if application_form_route -%} +{%- if doc.enable_admission_application -%}

    + href='/student-applicant'> {{ _("Apply Now") }}

    {% endif %} diff --git a/erpnext/templates/includes/cart/address_card.html b/erpnext/templates/includes/cart/address_card.html index c91723e91e..646210e65f 100644 --- a/erpnext/templates/includes/cart/address_card.html +++ b/erpnext/templates/includes/cart/address_card.html @@ -3,7 +3,7 @@
    -
    {{ address.name }}
    +
    {{ address.title }}

    {{ address.display }}

    diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html index 60de3af17b..aa25c885fe 100644 --- a/erpnext/templates/includes/cart/cart_address.html +++ b/erpnext/templates/includes/cart/cart_address.html @@ -109,7 +109,7 @@ frappe.ready(() => { reqd: 1 }, { - label: __('Pin Code'), + label: __('Postal Code'), fieldname: 'pincode', fieldtype: 'Data' }, diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index cf7661ee3f..82b2716a92 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -10,9 +10,18 @@ 'Agriculture': '/agriculture', 'Hospitality': '' } %} + {% set link = '' %} +{% set label = '' %} {% if domains %} - {% set link = links[domains[0].domain] %} + {% set label = domains[0].domain %} + {% set link = links[label] %} {% endif %} -Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + domains[0].domain + ' Companies') if domains else '' }} +{% if label == "Services" %} + {% set label = "Service" %} +{% endif %} + + + +Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + label + ' Companies') if domains else '' }} diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index 7960941e9e..b0fbf37a59 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -18,9 +18,9 @@ 90-Above,90-Dessus, A Customer Group exists with same name please change the Customer name or rename the Customer Group,"Un Groupe de Clients existe avec le même nom, veuillez changer le nom du Client ou renommer le Groupe de Clients", A Default Service Level Agreement already exists.,Un accord de niveau de service par défaut existe déjà., -A Lead requires either a person's name or an organization's name,Un responsable requiert le nom d'une personne ou le nom d'une organisation, +A Lead requires either a person's name or an organization's name,Un responsable requiert le nom d'une personne ou le nom d'une organisation, A customer with the same name already exists,Un client avec un nom identique existe déjà, -A question must have more than one options,Une question doit avoir plus d'une option, +A question must have more than one options,Une question doit avoir plus d'une option, A qustion must have at least one correct options,Une qustion doit avoir au moins une des options correctes, A {0} exists between {1} and {2} (,Un {0} existe entre {1} et {2} (, A4,A4, @@ -30,7 +30,7 @@ Abbr can not be blank or space,Abré. ne peut être vide ou contenir un espace, Abbreviation already used for another company,Abréviation déjà utilisée pour une autre société, Abbreviation cannot have more than 5 characters,L'abbréviation ne peut pas avoir plus de 5 caractères, Abbreviation is mandatory,Abréviation est obligatoire, -About the Company,À propos de l'entreprise, +About the Company,À propos de l'entreprise, About your company,A propos de votre entreprise, Above,Au-dessus, Absent,Absent, @@ -100,7 +100,7 @@ Active,actif, Active Leads / Customers,Prospects / Clients Actifs, Activity Cost exists for Employee {0} against Activity Type - {1},Des Coûts d'Activité existent pour l'Employé {0} pour le Type d'Activité - {1}, Activity Cost per Employee,Coût de l'Activité par Employé, -Activity Type,Type d'activité, +Activity Type,Type d'activité, Actual Cost,Prix actuel, Actual Delivery Date,Date de livraison réelle, Actual Qty,Quantité Réelle, @@ -129,7 +129,7 @@ Add Timesheets,Ajouter des feuilles de temps, Add Timeslots,Ajouter des Créneaux, Add Users to Marketplace,Ajouter des utilisateurs à la Marketplace, Add a new address,ajouter une nouvelle adresse, -Add cards or custom sections on homepage,Ajouter des cartes ou des sections personnalisées sur la page d'accueil, +Add cards or custom sections on homepage,Ajouter des cartes ou des sections personnalisées sur la page d'accueil, Add more items or open full form,Ajouter plus d'articles ou ouvrir le formulaire complet, Add notes,Ajouter des notes, Add the rest of your organization as your users. You can also add invite Customers to your portal by adding them from Contacts,Ajouter le reste de votre organisation en tant qu'utilisateurs. Vous pouvez aussi inviter des Clients sur votre portail en les ajoutant depuis les Contacts, @@ -196,7 +196,7 @@ All communications including and above this shall be moved into the new Issue,"T All items have already been invoiced,Tous les articles ont déjà été facturés, All items have already been transferred for this Work Order.,Tous les articles ont déjà été transférés pour cet ordre de travail., All other ITC,Tous les autres CTI, -All the mandatory Task for employee creation hasn't been done yet.,Toutes les tâches obligatoires pour la création d'employés n'ont pas encore été effectuées., +All the mandatory Task for employee creation hasn't been done yet.,Toutes les tâches obligatoires pour la création d'employés n'ont pas encore été effectuées., All these items have already been invoiced,Tous ces articles ont déjà été facturés, Allocate Payment Amount,Allouer le montant du paiement, Allocated Amount,Montant alloué, @@ -206,7 +206,7 @@ Allow Delete,Autoriser la suppression, Already record exists for the item {0},L'enregistrement existe déjà pour l'article {0}, "Already set default in pos profile {0} for user {1}, kindly disabled default","Déjà défini par défaut dans le profil pdv {0} pour l'utilisateur {1}, veuillez désactiver la valeur par défaut", Alternate Item,Article alternatif, -Alternative item must not be same as item code,L'article alternatif ne doit pas être le même que le code article, +Alternative item must not be same as item code,L'article alternatif ne doit pas être le même que le code article, Amended From,Modifié depuis, Amount,Montant, Amount After Depreciation,Montant après amortissement, @@ -221,14 +221,14 @@ Amount {0} {1} {2} {3},Montant {0} {1} {2} {3}, Amt,Nb, "An Item Group exists with same name, please change the item name or rename the item group","Un Groupe d'Article existe avec le même nom, veuillez changer le nom de l'article ou renommer le groupe d'article", An academic term with this 'Academic Year' {0} and 'Term Name' {1} already exists. Please modify these entries and try again.,Une période universitaire avec cette 'Année Universitaire' {0} et 'Nom de la Période' {1} existe déjà. Veuillez modifier ces entrées et essayer à nouveau., -An error occurred during the update process,Une erreur s'est produite lors du processus de mise à jour, +An error occurred during the update process,Une erreur s'est produite lors du processus de mise à jour, "An item exists with same name ({0}), please change the item group name or rename the item","Un article existe avec le même nom ({0}), veuillez changer le nom du groupe d'article ou renommer l'article", Analyst,Analyste, Analytics,Analytique, Annual Billing: {0},Facturation Annuelle : {0}, Annual Salary,Salaire annuel, Anonymous,Anonyme, -Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4},Un autre enregistrement Budget '{0}' existe déjà pour {1} '{2}' et pour le compte '{3}' pour l'exercice {4}., +Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4},Un autre enregistrement Budget '{0}' existe déjà pour {1} '{2}' et pour le compte '{3}' pour l'exercice {4}., Another Period Closing Entry {0} has been made after {1},Une autre Entrée de Clôture de Période {0} a été faite après {1}, Another Sales Person {0} exists with the same Employee id,Un autre Commercial {0} existe avec le même ID d'Employé, Antibiotic,Antibiotique, @@ -260,11 +260,11 @@ Approving User cannot be same as user the rule is Applicable To,L'Utilisateur Ap "Apps using current key won't be able to access, are you sure?","Les applications utilisant la clé actuelle ne pourront plus y accéder, êtes-vous sûr?", Are you sure you want to cancel this appointment?,Êtes-vous sûr de vouloir annuler ce rendez-vous?, Arrear,Arriéré, -As Examiner,En tant qu'examinateur, +As Examiner,En tant qu'examinateur, As On Date,Comme à la date, As Supervisor,En tant que superviseur, As per rules 42 & 43 of CGST Rules,Conformément aux règles 42 et 43 des règles de la CGST, -As per section 17(5),Conformément à l'article 17 (5), +As per section 17(5),Conformément à l'article 17 (5), As per your assigned Salary Structure you cannot apply for benefits,La struture salariale qui vous a été assignée ne vous permet pas de demander des avantages sociaux, Assessment,Évaluation, Assessment Criteria,Critères d'Évaluation, @@ -273,7 +273,7 @@ Assessment Group: ,Groupe d'Évaluation:, Assessment Plan,Plan d'Évaluation, Assessment Plan Name,Nom du Plan d'Évaluation, Assessment Report,Rapport d'Évaluation, -Assessment Reports,Rapports d'évaluation, +Assessment Reports,Rapports d'évaluation, Assessment Result,Résultat de l'Évaluation, Assessment Result record {0} already exists.,Le Résultat d'Évaluation {0} existe déjà., Asset,Atout, @@ -329,7 +329,7 @@ Available Selling,Vente disponible, Available for use date is required,La date de mise en service est nécessaire, Available slots,Créneaux Disponibles, Available {0},Disponible {0}, -Available-for-use Date should be after purchase date,La date de disponibilité devrait être postérieure à la date d'achat, +Available-for-use Date should be after purchase date,La date de disponibilité devrait être postérieure à la date d'achat, Average Age,Âge moyen, Average Rate,Prix moyen, Avg Daily Outgoing,Moy Quotidienne Sortante, @@ -384,7 +384,7 @@ Batch Name,Nom du lot, Batch No,N° du Lot, Batch number is mandatory for Item {0},Le numéro de lot est obligatoire pour l'Article {0}, Batch {0} of Item {1} has expired.,Lot {0} de l'Article {1} a expiré., -Batch {0} of Item {1} is disabled.,Le lot {0} de l'élément {1} est désactivé., +Batch {0} of Item {1} is disabled.,Le lot {0} de l'élément {1} est désactivé., Batch: ,Lot:, Batches,Lots, Become a Seller,Devenir vendeur, @@ -399,20 +399,20 @@ Billed,Facturé, Billed Amount,Montant facturé, Billing,Facturation, Billing Address,Adresse de facturation, -Billing Address is same as Shipping Address,L'adresse de facturation est identique à l'adresse de livraison, +Billing Address is same as Shipping Address,L'adresse de facturation est identique à l'adresse de livraison, Billing Amount,Montant de Facturation, Billing Status,Statut de la Facturation, Billing currency must be equal to either default company's currency or party account currency,La devise de facturation doit être égale à la devise de la société par défaut ou à la devise du compte du partenaire, Bills raised by Suppliers.,Factures émises par des Fournisseurs., Bills raised to Customers.,Factures émises pour des Clients., Biotechnology,Biotechnologie, -Birthday Reminder,Rappel d'anniversaire, +Birthday Reminder,Rappel d'anniversaire, Black,Noir, Blanket Orders from Costumers.,Commandes provisoires de clients., Block Invoice,Bloquer la facture, Boms,Listes de Matériaux, Bonus Payment Date cannot be a past date,La date de paiement du bonus ne peut pas être une date passée, -Both Trial Period Start Date and Trial Period End Date must be set,La date de début de la période d'essai et la date de fin de la période d'essai doivent être définies, +Both Trial Period Start Date and Trial Period End Date must be set,La date de début de la période d'essai et la date de fin de la période d'essai doivent être définies, Both Warehouse must belong to same Company,Les deux Entrepôt doivent appartenir à la même Société, Branch,Branche, Broadcasting,Radio/Télévision, @@ -429,7 +429,7 @@ Business Development Manager,Directeur Commercial, Buy,Acheter, Buying,Achat, Buying Amount,Montant d'Achat, -Buying Price List,Liste de prix d'achat, +Buying Price List,Liste de prix d'achat, Buying Rate,Prix d'achat, "Buying must be checked, if Applicable For is selected as {0}","Achat doit être vérifié, si Applicable Pour {0} est sélectionné", By {0},Par {0}, @@ -447,7 +447,7 @@ Campaign,Campagne, Can be approved by {0},Peut être approuvé par {0}, "Can not filter based on Account, if grouped by Account","Impossible de filtrer sur le Compte , si les lignes sont regroupées par Compte", "Can not filter based on Voucher No, if grouped by Voucher","Impossible de filtrer sur la base du N° de Coupon, si les lignes sont regroupées par Coupon", -"Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}","Impossible de marquer le dossier d'hospitalisation déchargé, il existe des factures non facturées {0}", +"Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}","Impossible de marquer le dossier d'hospitalisation déchargé, il existe des factures non facturées {0}", Can only make payment against unbilled {0},Le paiement n'est possible qu'avec les {0} non facturés, Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total',Peut se référer à ligne seulement si le type de charge est 'Montant de la ligne précedente' ou 'Total des lignes précedente', "Can't change valuation method, as there are transactions against some items which does not have it's own valuation method","Impossible de modifier la méthode de valorisation, car il existe des transactions sur certains articles ne possèdant pas leur propre méthode de valorisation", @@ -455,17 +455,17 @@ Can't create standard criteria. Please rename the criteria,Impossible de créer Cancel,Annuler, Cancel Material Visit {0} before cancelling this Warranty Claim,Annuler la Visite Matérielle {0} avant d'annuler cette Réclamation de Garantie, Cancel Material Visits {0} before cancelling this Maintenance Visit,Annuler les Visites Matérielles {0} avant d'annuler cette Visite de Maintenance, -Cancel Subscription,Annuler l'abonnement, +Cancel Subscription,Annuler l'abonnement, Cancel the journal entry {0} first,Annuler d'abord l'écriture de journal {0}, Canceled,Annulé, "Cannot Submit, Employees left to mark attendance","Ne peut pas être soumis, certains employés n'ont pas pas validé leurs feuilles de présence", Cannot be a fixed asset item as Stock Ledger is created.,Ne peut pas être un article immobilisé car un Journal de Stock a été créé., Cannot cancel because submitted Stock Entry {0} exists,Impossible d'annuler car l'Écriture de Stock soumise {0} existe, Cannot cancel transaction for Completed Work Order.,Impossible d'annuler la transaction lorsque l'ordre de travail est terminé., -Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3},Impossible d'annuler {0} {1} car le numéro de série {2} n'appartient pas à l'entrepôt {3}, +Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3},Impossible d'annuler {0} {1} car le numéro de série {2} n'appartient pas à l'entrepôt {3}, Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item,Impossible de modifier les attributs après des mouvements de stock. Faites un nouvel article et transférez la quantité en stock au nouvel article, Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved.,Impossible de modifier les dates de début et de fin d'exercice une fois que l'exercice est enregistré., -Cannot change Service Stop Date for item in row {0},Impossible de modifier la date d'arrêt du service pour l'élément de la ligne {0}, +Cannot change Service Stop Date for item in row {0},Impossible de modifier la date d'arrêt du service pour l'élément de la ligne {0}, Cannot change Variant properties after stock transaction. You will have to make a new Item to do this.,Impossible de modifier les propriétés de variante après une transaction de stock. Vous devrez créer un nouvel article pour pouvoir le faire., "Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency.","Impossible de changer la devise par défaut de la société, parce qu'il y a des opérations existantes. Les transactions doivent être annulées pour changer la devise par défaut.", Cannot change status as student {0} is linked with student application {1},Impossible de changer le statut car l'étudiant {0} est lié à la candidature de l'étudiant {1}, @@ -479,7 +479,7 @@ Cannot deduct when category is for 'Valuation' or 'Valuation and Total',Déducti Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',Vous ne pouvez pas déduire lorsqu'une catégorie est pour 'Évaluation' ou 'Évaluation et Total', "Cannot delete Serial No {0}, as it is used in stock transactions","Impossible de supprimer les N° de série {0}, s'ils sont dans les mouvements de stock", Cannot enroll more than {0} students for this student group.,Inscription de plus de {0} étudiants impossible pour ce groupe d'étudiants., -Cannot find Item with this barcode,Impossible de trouver l'article avec ce code à barres, +Cannot find Item with this barcode,Impossible de trouver l'article avec ce code à barres, Cannot find active Leave Period,Impossible de trouver une période de congés active, Cannot produce more Item {0} than Sales Order quantity {1},Impossible de produire plus d'Article {0} que la quantité {1} du Bon de Commande, Cannot promote Employee with status Left,"Impossible de promouvoir un employé avec le statut ""Parti""", @@ -538,7 +538,7 @@ Cheques Required,Chèques requis, Cheques and Deposits incorrectly cleared,Chèques et Dépôts incorrectement compensés, Child Item should not be a Product Bundle. Please remove item `{0}` and save,Le sous-article ne doit pas être un ensemble de produit. S'il vous plaît retirer l'article `{0}` et sauver, Child Task exists for this Task. You can not delete this Task.,Une tâche enfant existe pour cette tâche. Vous ne pouvez pas supprimer cette tâche., -Child nodes can be only created under 'Group' type nodes,Les noeuds enfants peuvent être créés uniquement dans les nœuds de type 'Groupe', +Child nodes can be only created under 'Group' type nodes,Les noeuds enfants peuvent être créés uniquement dans les nœuds de type 'Groupe', Child warehouse exists for this warehouse. You can not delete this warehouse.,Un entrepôt enfant existe pour cet entrepôt. Vous ne pouvez pas supprimer cet entrepôt., Circular Reference Error,Erreur de référence circulaire, City,Ville, @@ -578,11 +578,11 @@ Commission rate cannot be greater than 100,Taux de commission ne peut pas être Community Forum,Forum de la communauté, Company (not Customer or Supplier) master.,Données de base de la Société (ni les Clients ni les Fournisseurs), Company Abbreviation,Abréviation de la Société, -Company Abbreviation cannot have more than 5 characters,L'abréviation de l'entreprise ne peut pas comporter plus de 5 caractères, +Company Abbreviation cannot have more than 5 characters,L'abréviation de l'entreprise ne peut pas comporter plus de 5 caractères, Company Name,Nom de la Société, Company Name cannot be Company,Nom de la Société ne peut pas être Company, Company currencies of both the companies should match for Inter Company Transactions.,Les devises des deux sociétés doivent correspondre pour les transactions inter-sociétés., -Company is manadatory for company account,La société est le maître d'œuvre du compte d'entreprise, +Company is manadatory for company account,La société est le maître d'œuvre du compte d'entreprise, Company name not same,Le nom de la société n'est pas identique, Company {0} does not exist,Société {0} n'existe pas, "Company, Payment Account, From Date and To Date is mandatory","Société, compte de paiement, date de début et date de fin sont obligatoires", @@ -653,7 +653,7 @@ Could not submit some Salary Slips,Les fiches de paie n'ont pas pu être soumise Country wise default Address Templates,Modèles d'Adresse par défaut en fonction du pays, Course,Cours, Course Code: ,Code du Cours:, -Course Enrollment {0} does not exists,L'inscription au cours {0} n'existe pas, +Course Enrollment {0} does not exists,L'inscription au cours {0} n'existe pas, Course Schedule,Horaire du cours, Course: ,Cours:, Cr,Cr, @@ -677,7 +677,7 @@ Create Leads,Créer des Prospects, Create Maintenance Visit,Créer une visite de maintenance, Create Material Request,Créer une demande de matériel, Create Multiple,Créer plusieurs, -Create Opening Sales and Purchase Invoices,Créer des factures d'ouverture et des factures d'achat, +Create Opening Sales and Purchase Invoices,Créer des factures d'ouverture et des factures d'achat, Create Payment Entries,Créer des entrées de paiement, Create Payment Entry,Créer une entrée de paiement, Create Print Format,Créer Format d'Impression, @@ -691,7 +691,7 @@ Create Sales Order,Créer une commande client, Create Sales Orders to help you plan your work and deliver on-time,Créez des commandes pour vous aider à planifier votre travail et à livrer à temps, Create Sample Retention Stock Entry,Créer un échantillon de stock de rétention, Create Student,Créer un étudiant, -Create Student Batch,Créer un lot d'étudiants, +Create Student Batch,Créer un lot d'étudiants, Create Student Groups,Créer des Groupes d'Étudiants, Create Supplier Quotation,Créer une offre fournisseur, Create Tax Template,Créer un modèle de taxe, @@ -706,11 +706,11 @@ Create customer quotes,Créer les devis client, Create rules to restrict transactions based on values.,Créer des règles pour restreindre les transactions basées sur les valeurs ., Created By,Établi par, Created {0} scorecards for {1} between: ,{0} fiches d'évaluations créées pour {1} entre:, -Creating Company and Importing Chart of Accounts,Création d'une société et importation d'un plan comptable, +Creating Company and Importing Chart of Accounts,Création d'une société et importation d'un plan comptable, Creating Fees,Création d'Honoraires, Creating Payment Entries......,Créer des écritures de paiement..., Creating Salary Slips...,Création des fiches de paie en cours..., -Creating student groups,Créer des groupes d'étudiants, +Creating student groups,Créer des groupes d'étudiants, Creating {0} Invoice,Création de {0} facture, Credit,Crédit, Credit ({0}),Crédit ({0}), @@ -763,7 +763,7 @@ Customer required for 'Customerwise Discount',Client requis pour appliquer une ' Customer {0} does not belong to project {1},Le Client {0} ne fait pas parti du projet {1}, Customer {0} is created.,Le client {0} est créé., Customers in Queue,Clients dans la File d'Attente, -Customize Homepage Sections,Personnaliser les sections de la page d'accueil, +Customize Homepage Sections,Personnaliser les sections de la page d'accueil, Customizing Forms,Personnalisation des formulaires, Daily Project Summary for {0},Récapitulatif quotidien du projet pour {0}, Daily Reminders,Rappels quotidiens, @@ -817,7 +817,7 @@ Del,Supp, Delay in payment (Days),Retard de paiement (jours), Delete all the Transactions for this Company,Supprimer toutes les transactions pour cette société, Delete permanently?,Supprimer définitivement ?, -Deletion is not permitted for country {0},La suppression n'est pas autorisée pour le pays {0}, +Deletion is not permitted for country {0},La suppression n'est pas autorisée pour le pays {0}, Delivered,Livré, Delivered Amount,Montant Livré, Delivered Qty,Qté Livrée, @@ -842,13 +842,13 @@ Depreciation Eliminated due to disposal of assets,Amortissement Eliminé en rais Depreciation Entry,Ecriture d’Amortissement, Depreciation Method,Méthode d'Amortissement, Depreciation Row {0}: Depreciation Start Date is entered as past date,Ligne de d'amortissement {0}: La date de début de l'amortissement est dans le passé, -Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1},Ligne d'amortissement {0}: la valeur attendue après la durée de vie utile doit être supérieure ou égale à {1}, +Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1},Ligne d'amortissement {0}: la valeur attendue après la durée de vie utile doit être supérieure ou égale à {1}, Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date,Ligne d'amortissement {0}: La date d'amortissement suivante ne peut pas être antérieure à la date de mise en service, -Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date,Ligne d'amortissement {0}: la date d'amortissement suivante ne peut pas être antérieure à la date d'achat, +Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date,Ligne d'amortissement {0}: la date d'amortissement suivante ne peut pas être antérieure à la date d'achat, Designer,Designer, Detailed Reason,Raison détaillée, Details,Détails, -Details of Outward Supplies and inward supplies liable to reverse charge,Détails des livraisons sortantes et des livraisons entrantes susceptibles d'inverser la charge, +Details of Outward Supplies and inward supplies liable to reverse charge,Détails des livraisons sortantes et des livraisons entrantes susceptibles d'inverser la charge, Details of the operations carried out.,Détails des opérations effectuées., Diagnosis,Diagnostique, Did not find any item called {0},N'a pas trouvé d'élément appelé {0}, @@ -872,7 +872,7 @@ Discount amount cannot be greater than 100%,Le montant de la réduction ne peut Discount must be less than 100,La remise doit être inférieure à 100, Diseases & Fertilizers,Maladies et engrais, Dispatch,Envoi, -Dispatch Notification,Notification d'expédition, +Dispatch Notification,Notification d'expédition, Dispatch State,Statut de l'expédition, Distance,Distance, Distribution,Distribution, @@ -900,7 +900,7 @@ Draft,Brouillon, Drop Ship,Expédition Directe, Drug,Médicament, Due / Reference Date cannot be after {0},Date d’échéance / de référence ne peut pas être après le {0}, -Due Date cannot be before Posting / Supplier Invoice Date,La date d'échéance ne peut pas être antérieure à la date de comptabilisation / facture fournisseur, +Due Date cannot be before Posting / Supplier Invoice Date,La date d'échéance ne peut pas être antérieure à la date de comptabilisation / facture fournisseur, Due Date is mandatory,La Date d’Échéance est obligatoire, Duplicate Entry. Please check Authorization Rule {0},Écriture en double. Merci de vérifier la Règle d’Autorisation {0}, Duplicate Serial No entered for Item {0},Dupliquer N° de Série pour l'Article {0}, @@ -920,7 +920,7 @@ Earnest Money,Arrhes, Earning,Revenus, Edit,modifier, Edit Publishing Details,Modifier les détails de publication, -"Edit in full page for more options like assets, serial nos, batches etc.","Modifier en pleine page pour plus d'options comme les actifs, les numéros de série, les lots, etc.", +"Edit in full page for more options like assets, serial nos, batches etc.","Modifier en pleine page pour plus d'options comme les actifs, les numéros de série, les lots, etc.", Education,Éducation, Either location or employee must be required,La localisation ou l'employé sont requis, Either target qty or target amount is mandatory,Soit la qté cible soit le montant cible est obligatoire, @@ -935,16 +935,16 @@ Email Address,Adresse électronique, Email Digest: ,Compte Rendu par Email :, Email Reminders will be sent to all parties with email contacts,Les rappels par emails seront envoyés à toutes les parties avec des contacts ayant une adresse email, Email Sent,Email envoyé, -Email Template,Modèle d'email, +Email Template,Modèle d'email, Email not found in default contact,Email non trouvé dans le contact par défaut, Email sent to supplier {0},Email envoyé au fournisseur {0}, Email sent to {0},Email envoyé à {0}, Employee,Employé, -Employee A/C Number,Numéro de l'employé, +Employee A/C Number,Numéro de l'employé, Employee Advances,Avances versées aux employés, Employee Benefits,Avantages de l'Employé, Employee Grade,Echelon des employés, -Employee ID,Numéro d'employé, +Employee ID,Numéro d'employé, Employee Lifecycle,Cycle de vie des employés, Employee Name,Nom de l'Employé, Employee Promotion cannot be submitted before Promotion Date ,La promotion ne peut être soumise avant la date de promotion, @@ -952,9 +952,9 @@ Employee Referral,Recommandations, Employee Transfer cannot be submitted before Transfer Date ,Le transfert ne peut pas être soumis avant la date de transfert, Employee cannot report to himself.,L'employé ne peut pas rendre de compte à lui-même., Employee relieved on {0} must be set as 'Left',Employé dégagé de {0} doit être défini comme 'Gauche', -Employee status cannot be set to 'Left' as following employees are currently reporting to this employee: ,Le statut d'employé ne peut pas être défini sur 'Gauche' car les employés suivants sont actuellement rattachés à cet employé:, +Employee status cannot be set to 'Left' as following employees are currently reporting to this employee: ,Le statut d'employé ne peut pas être défini sur 'Gauche' car les employés suivants sont actuellement rattachés à cet employé:, Employee {0} already submited an apllication {1} for the payroll period {2},L'employé {0} a déjà envoyé une demande {1} pour la période de calcul de paie {2}, -Employee {0} has already applied for {1} between {2} and {3} : ,L'employé {0} a déjà postulé pour {1} entre {2} et {3}:, +Employee {0} has already applied for {1} between {2} and {3} : ,L'employé {0} a déjà postulé pour {1} entre {2} et {3}:, Employee {0} has already applied for {1} on {2} : ,L'employé {0} a déjà postulé pour {1} le {2}:, Employee {0} has no maximum benefit amount,L'employé {0} n'a pas de montant maximal d'avantages sociaux, Employee {0} is not active or does not exist,"L'employé {0} n'est pas actif, ou n'existe pas", @@ -971,7 +971,7 @@ End Date cannot be before Start Date.,La date de fin ne peut pas être antérieu End Year,Année de Fin, End Year cannot be before Start Year,L'Année de Fin ne peut pas être avant l'Année de Début, End on,Termine le, -End time cannot be before start time,L'heure de fin ne peut pas être avant l'heure de début, +End time cannot be before start time,L'heure de fin ne peut pas être avant l'heure de début, Ends On date cannot be before Next Contact Date.,La date de fin ne peut pas être avant la prochaine date de contact, Energy,Énergie, Engineer,Ingénieur, @@ -1021,7 +1021,7 @@ Expense Claims,Notes de Frais, Expense account is mandatory for item {0},Compte de charge est obligatoire pour l'article {0}, Expense or Difference account is mandatory for Item {0} as it impacts overall stock value,Compte de Charge et d'Écarts est obligatoire pour objet {0} car il impacte la valeur globale des actions, Expenses,Charges, -Expenses Included In Asset Valuation,Dépenses incluses dans l'évaluation de l'actif, +Expenses Included In Asset Valuation,Dépenses incluses dans l'évaluation de l'actif, Expenses Included In Valuation,Charges Incluses dans la Valorisation, Expired Batches,Lots expirés, Expires On,Expire le, @@ -1034,7 +1034,7 @@ Extra Small,Très Petit, Fail,Échec, Failed,Échoué, Failed to create website,Échec de la création du site Web, -Failed to install presets,Échec de l'installation des préréglages, +Failed to install presets,Échec de l'installation des préréglages, Failed to login,Échec de la connexion, Failed to setup company,Échec de la configuration de la société, Failed to setup defaults,Échec de la configuration par défaut, @@ -1066,16 +1066,16 @@ Financial Statements,États financiers, Financial Year,Exercice Financier, Finish,terminer, Finished Good,Produit fini, -Finished Good Item Code,Code d'article fini, +Finished Good Item Code,Code d'article fini, Finished Goods,Produits finis, Finished Item {0} must be entered for Manufacture type entry,Le Produit Fini {0} doit être saisi pour une écriture de type Production, Finished product quantity {0} and For Quantity {1} cannot be different,La quantité de produit fini {0} et Pour la quantité {1} ne peut pas être différente, First Name,Prénom, -"Fiscal Regime is mandatory, kindly set the fiscal regime in the company {0}","Le régime fiscal est obligatoire, veuillez définir le régime fiscal de l'entreprise {0}", +"Fiscal Regime is mandatory, kindly set the fiscal regime in the company {0}","Le régime fiscal est obligatoire, veuillez définir le régime fiscal de l'entreprise {0}", Fiscal Year,Exercice fiscal, -Fiscal Year End Date should be one year after Fiscal Year Start Date,La date de fin d'exercice doit être un an après la date de début d'exercice, +Fiscal Year End Date should be one year after Fiscal Year Start Date,La date de fin d'exercice doit être un an après la date de début d'exercice, Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0},La Date de Début et la Date de Fin de l'Exercice Fiscal sont déjà définies dans l'Année Fiscale {0}, -Fiscal Year Start Date should be one year earlier than Fiscal Year End Date,La date de début d'exercice doit être un an plus tôt que la date de fin d'exercice, +Fiscal Year Start Date should be one year earlier than Fiscal Year End Date,La date de début d'exercice doit être un an plus tôt que la date de fin d'exercice, Fiscal Year {0} does not exist,Exercice Fiscal {0} n'existe pas, Fiscal Year {0} is required,Exercice Fiscal {0} est nécessaire, Fiscal Year {0} not found,Exercice Fiscal {0} introuvable, @@ -1086,8 +1086,8 @@ Fixed Assets,Actifs Immobilisés, Following Material Requests have been raised automatically based on Item's re-order level,Les Demandes de Matériel suivantes ont été créées automatiquement sur la base du niveau de réapprovisionnement de l’Article, Following accounts might be selected in GST Settings:,Les comptes suivants peuvent être sélectionnés dans les paramètres GST:, Following course schedules were created,Les horaires de cours suivants ont été créés, -Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master,L'élément suivant {0} n'est pas marqué comme élément {1}. Vous pouvez les activer en tant qu'élément {1} à partir de sa fiche article., -Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master,Les éléments suivants {0} ne sont pas marqués comme {1} élément. Vous pouvez les activer en tant qu'élément {1} à partir de sa fiche article., +Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master,L'élément suivant {0} n'est pas marqué comme élément {1}. Vous pouvez les activer en tant qu'élément {1} à partir de sa fiche article., +Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master,Les éléments suivants {0} ne sont pas marqués comme {1} élément. Vous pouvez les activer en tant qu'élément {1} à partir de sa fiche article., Food,Alimentation, "Food, Beverage & Tobacco","Alimentation, boissons et tabac", For,Pour, @@ -1099,14 +1099,14 @@ For Warehouse,Pour l’Entrepôt, For Warehouse is required before Submit,Pour l’Entrepôt est requis avant de Soumettre, "For an item {0}, quantity must be negative number","Pour l'article {0}, la quantité doit être un nombre négatif", "For an item {0}, quantity must be positive number","Pour un article {0}, la quantité doit être un nombre positif", -"For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry","Pour la carte de travail {0}, vous pouvez uniquement saisir une entrée de stock de type "Transfert d'article pour fabrication".", +"For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry","Pour la carte de travail {0}, vous pouvez uniquement saisir une entrée de stock de type "Transfert d'article pour fabrication".", "For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included","Pour la ligne {0} dans {1}. Pour inclure {2} dans le prix de l'Article, les lignes {3} doivent également être incluses", For row {0}: Enter Planned Qty,Pour la ligne {0}: entrez la quantité planifiée, "For {0}, only credit accounts can be linked against another debit entry","Pour {0}, seuls les comptes de crédit peuvent être liés avec une autre écriture de débit", "For {0}, only debit accounts can be linked against another credit entry","Pour {0}, seuls les comptes de débit peuvent être liés avec une autre écriture de crédit", Form View,Vue de Formulaire, Forum Activity,Activité du forum, -Free item code is not selected,Le code d'article gratuit n'est pas sélectionné, +Free item code is not selected,Le code d'article gratuit n'est pas sélectionné, Freight and Forwarding Charges,Frais de Fret et d'Expédition, Frequency,Fréquence, Friday,Vendredi, @@ -1122,7 +1122,7 @@ From Date {0} cannot be after employee's relieving Date {1},La date de début {0 From Date {0} cannot be before employee's joining Date {1},La date de départ {0} ne peut pas être antérieure à la date d'arrivée de l'employé {1}, From Datetime,A partir du (Date et Heure), From Delivery Note,Du Bon de Livraison, -From Fiscal Year,À partir de l'année fiscale, +From Fiscal Year,À partir de l'année fiscale, From GSTIN,GSTIN (Origine), From Party Name,Nom du tiers (Origine), From Pin Code,Code postal (Origine), @@ -1132,7 +1132,7 @@ From State,Etat (Origine), From Time,Horaire de Début, From Time Should Be Less Than To Time,Du temps devrait être moins que du temps, From Time cannot be greater than To Time.,L’Horaire Initial ne peut pas être postérieur à l’Horaire Final, -"From a supplier under composition scheme, Exempt and Nil rated","De la part d'un fournisseur sous schéma de composition, coté Exempt et Nil", +"From a supplier under composition scheme, Exempt and Nil rated","De la part d'un fournisseur sous schéma de composition, coté Exempt et Nil", From and To dates required,Les date Du et Au sont requises, From date can not be less than employee's joining date,La date de départ ne peut être antérieure à la date d'arrivée de l'employé, From value must be less than to value in row {0},De la valeur doit être inférieure à la valeur de la ligne {0}, @@ -1184,7 +1184,7 @@ Goals cannot be empty,Les objectifs ne peuvent pas être vides, Goods In Transit,Les marchandises en transit, Goods Transferred,Marchandises transférées, Goods and Services Tax (GST India),Taxe sur les Biens et Services (GST India), -Goods are already received against the outward entry {0},Les marchandises sont déjà reçues pour l'entrée sortante {0}, +Goods are already received against the outward entry {0},Les marchandises sont déjà reçues pour l'entrée sortante {0}, Government,Gouvernement, Grand Total,Total TTC, Grant,Subvention, @@ -1229,11 +1229,11 @@ Health Care,Soins de santé, Healthcare,Santé, Healthcare (beta),Santé (beta), Healthcare Practitioner,Praticien de la santé, -Healthcare Practitioner not available on {0},Le praticien de la santé n'est pas disponible le {0}, -Healthcare Practitioner {0} not available on {1},Le praticien de la santé {0} n'est pas disponible le {1}, +Healthcare Practitioner not available on {0},Le praticien de la santé n'est pas disponible le {0}, +Healthcare Practitioner {0} not available on {1},Le praticien de la santé {0} n'est pas disponible le {1}, Healthcare Service Unit,Service de soins de santé, Healthcare Service Unit Tree,Arbre des services de soins de santé, -Healthcare Service Unit Type,Type d'unité de service de soins de santé, +Healthcare Service Unit Type,Type d'unité de service de soins de santé, Healthcare Services,Services de santé, Healthcare Settings,Paramètres de santé, Hello,Bonjour, @@ -1244,7 +1244,7 @@ Hold,Tenir, Hold Invoice,Facture en attente, Holiday,Vacances, Holiday List,Liste de vacances, -Hotel Rooms of type {0} are unavailable on {1},Les chambres d'hôtel de type {0} sont indisponibles le {1}, +Hotel Rooms of type {0} are unavailable on {1},Les chambres d'hôtel de type {0} sont indisponibles le {1}, Hotels,Hôtels, Hourly,Horaire, Hours,Heures, @@ -1272,7 +1272,7 @@ Ignore Existing Ordered Qty,Ignorer la quantité commandée existante, Image,Image, Image View,Voir l'Image, Import Data,Importer des données, -Import Day Book Data,Données du journal d'importation, +Import Day Book Data,Données du journal d'importation, Import Log,Journal d'import, Import Master Data,Importer des données de base, Import Successfull,Importation réussie, @@ -1308,14 +1308,14 @@ Indirect Income,Revenu indirect, Individual,Individuel, Ineligible ITC,CTI non éligible, Initiated,Initié, -Inpatient Record,Dossier d'hospitalisation, +Inpatient Record,Dossier d'hospitalisation, Insert,Insérer, Installation Note,Note d'Installation, Installation Note {0} has already been submitted,Note d'Installation {0} à déjà été sousmise, Installation date cannot be before delivery date for Item {0},Date d'installation ne peut pas être avant la date de livraison pour l'Article {0}, Installing presets,Installation des réglages, Institute Abbreviation,Abréviation de l'Institut, -Institute Name,Nom de l'Institut, +Institute Name,Nom de l'Institut, Instructor,Instructeur, Insufficient Stock,Stock insuffisant, Insurance Start date should be less than Insurance End date,Date de Début d'Assurance devrait être antérieure à la Date de Fin d'Assurance, @@ -1331,8 +1331,8 @@ Invalid Attribute,Attribut invalide, Invalid Blanket Order for the selected Customer and Item,Commande avec limites non valide pour le client et l'article sélectionnés, Invalid Company for Inter Company Transaction.,Société non valide pour une transaction inter-sociétés., Invalid GSTIN! A GSTIN must have 15 characters.,GSTIN invalide! Un GSTIN doit comporter 15 caractères., -Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,GSTIN invalide! Les deux premiers chiffres de GSTIN doivent correspondre au numéro d'état {0}., -Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,GSTIN invalide! L'entrée que vous avez entrée ne correspond pas au format de GSTIN., +Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,GSTIN invalide! Les deux premiers chiffres de GSTIN doivent correspondre au numéro d'état {0}., +Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,GSTIN invalide! L'entrée que vous avez entrée ne correspond pas au format de GSTIN., Invalid Posting Time,Heure de publication non valide, Invalid attribute {0} {1},Attribut invalide {0} {1}, Invalid quantity specified for item {0}. Quantity should be greater than 0.,Quantité spécifiée invalide pour l'élément {0}. Quantité doit être supérieur à 0., @@ -1351,12 +1351,12 @@ Invoice Posting Date,Date d’Envois de la Facture, Invoice Type,Type de facture, Invoice already created for all billing hours,Facture déjà créée pour toutes les heures facturées, Invoice can't be made for zero billing hour,La facture ne peut pas être faite pour une heure facturée à zéro, -Invoice {0} no longer exists,La facture {0} n'existe plus, +Invoice {0} no longer exists,La facture {0} n'existe plus, Invoiced,Facturé, Invoiced Amount,Montant facturé, Invoices,Factures, Invoices for Costumers.,Factures pour les clients., -Inward Supplies(liable to reverse charge,Approvisionnement entrant (susceptible d'inverser la charge, +Inward Supplies(liable to reverse charge,Approvisionnement entrant (susceptible d'inverser la charge, Inward supplies from ISD,Approvisionnement entrant de la DSI, Inward supplies liable to reverse charge (other than 1 & 2 above),Approvisionnements entrants susceptibles d’être dédouanés (autres que 1 et 2 ci-dessus), Is Active,Est Active, @@ -1383,17 +1383,17 @@ Item Description,Description de l'Article, Item Group,Groupe d'Article, Item Group Tree,Arborescence de Groupe d'Article, Item Group not mentioned in item master for item {0},Le Groupe d'Articles n'est pas mentionné dans la fiche de l'article pour l'article {0}, -Item Name,Nom de l'article, +Item Name,Nom de l'article, Item Price added for {0} in Price List {1},Prix de l'Article ajouté pour {0} dans la Liste de Prix {1}, -"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates.","Le prix de l'article apparaît plusieurs fois en fonction de la liste de prix, du fournisseur / client, de la devise, de l'article, de l'unité de mesure, de la quantité et des dates.", +"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates.","Le prix de l'article apparaît plusieurs fois en fonction de la liste de prix, du fournisseur / client, de la devise, de l'article, de l'unité de mesure, de la quantité et des dates.", Item Price updated for {0} in Price List {1},Prix de l'Article mis à jour pour {0} dans la Liste des Prix {1}, -Item Row {0}: {1} {2} does not exist in above '{1}' table,Ligne d'objet {0}: {1} {2} n'existe pas dans la table '{1}' ci-dessus, +Item Row {0}: {1} {2} does not exist in above '{1}' table,Ligne d'objet {0}: {1} {2} n'existe pas dans la table '{1}' ci-dessus, Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable,La Ligne de Taxe d'Article {0} doit indiquer un compte de type Taxes ou Produit ou Charge ou Facturable, -Item Template,Modèle d'article, +Item Template,Modèle d'article, Item Variant Settings,Paramètres de Variante d'Article, Item Variant {0} already exists with same attributes,La Variante de l'Article {0} existe déjà avec les mêmes caractéristiques, Item Variants,Variantes de l'Article, -Item Variants updated,Variantes d'article mises à jour, +Item Variants updated,Variantes d'article mises à jour, Item has variants.,L'article a des variantes., Item must be added using 'Get Items from Purchase Receipts' button,L'article doit être ajouté à l'aide du bouton 'Obtenir des éléments de Reçus d'Achat', Item or Warehouse for row {0} does not match Material Request,L'Article ou l'Entrepôt pour la ligne {0} ne correspond pas avec la Requête de Matériel, @@ -1422,12 +1422,12 @@ Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1},Artic Item {0}: Ordered qty {1} cannot be less than minimum order qty {2} (defined in Item).,L'article {0} : Qté commandée {1} ne peut pas être inférieure à la qté de commande minimum {2} (défini dans l'Article)., Item: {0} does not exist in the system,Article : {0} n'existe pas dans le système, Items,Articles, -Items Filter,Filtre d'articles, +Items Filter,Filtre d'articles, Items and Pricing,Articles et prix, Items for Raw Material Request,Articles pour demande de matière première, Job Card,Carte de travail, Job Description,Description de l'Emploi, -Job Offer,Offre d'emploi, +Job Offer,Offre d'emploi, Job card {0} created,Job card {0} créée, Jobs,Emplois, Join,Joindre, @@ -1455,7 +1455,7 @@ Last Communication Date,Date de la Dernière Communication, Last Name,Nom de famille, Last Order Amount,Montant de la Dernière Commande, Last Order Date,Date de la dernière commande, -Last Purchase Price,Dernier prix d'achat, +Last Purchase Price,Dernier prix d'achat, Last Purchase Rate,Dernier Prix d'Achat, Latest,Dernier, Latest price updated in all BOMs,Prix les plus récents mis à jour dans toutes les LDMs, @@ -1479,7 +1479,7 @@ Leave Type {0} cannot be carry-forwarded,Le Type de Congé {0} ne peut pas être Leave Type {0} is not encashable,Le type de congé {0} n'est pas encaissable, Leave Without Pay,Congé Sans Solde, Leave and Attendance,Congés et Présences, -Leave application {0} already exists against the student {1},Laisser l'application {0} existe déjà pour l'étudiant {1}, +Leave application {0} already exists against the student {1},Laisser l'application {0} existe déjà pour l'étudiant {1}, "Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}","Congé ne peut être alloué avant le {0}, car le solde de congés a déjà été reporté dans la feuille d'allocation de congés futurs {1}", "Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}","Congé ne peut être demandé / annulé avant le {0}, car le solde de congés a déjà été reporté dans la feuille d'allocation de congés futurs {1}", Leave of type {0} cannot be longer than {1},Les Congés de type {0} ne peuvent pas être plus long que {1}, @@ -1543,7 +1543,7 @@ Maintenance Visit {0} must be cancelled before cancelling this Sales Order,La Vi Maintenance start date can not be before delivery date for Serial No {0},La date de début d'entretien ne peut pas être antérieure à la date de livraison pour le N° de Série {0}, Make,Faire, Make Payment,Faire un Paiement, -Make project from a template.,Faire un projet à partir d'un modèle., +Make project from a template.,Faire un projet à partir d'un modèle., Making Stock Entries,Faire des Écritures de Stock, Male,Masculin, Manage Customer Group Tree.,Gérer l'Arborescence des Groupes de Clients., @@ -1593,16 +1593,16 @@ Material Request {0} submitted.,Demande de matériel {0} soumise., Material Transfer,Transfert de matériel, Material Transferred,Matériel transféré, Material to Supplier,Du Matériel au Fournisseur, -Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1},Le montant maximal de l'exemption ne peut pas dépasser le montant maximal de l'exonération {0} de la catégorie d'exonération fiscale {1}., +Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1},Le montant maximal de l'exemption ne peut pas dépasser le montant maximal de l'exonération {0} de la catégorie d'exonération fiscale {1}., Max benefits should be greater than zero to dispense benefits,Les prestations sociales maximales doivent être supérieures à zéro pour être calculées, Max discount allowed for item: {0} is {1}%,Réduction max autorisée pour l'article : {0} est de {1} %, Max: {0},Max : {0}, -Maximum Samples - {0} can be retained for Batch {1} and Item {2}.,Maximum d'échantillons - {0} peut être conservé pour le lot {1} et l'article {2}., -Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}.,Nombre maximum d'échantillons - {0} ont déjà été conservés pour le lot {1} et l'article {2} dans le lot {3}., +Maximum Samples - {0} can be retained for Batch {1} and Item {2}.,Maximum d'échantillons - {0} peut être conservé pour le lot {1} et l'article {2}., +Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}.,Nombre maximum d'échantillons - {0} ont déjà été conservés pour le lot {1} et l'article {2} dans le lot {3}., Maximum amount eligible for the component {0} exceeds {1},Le montant maximal éligible pour le composant {0} dépasse {1}, Maximum benefit amount of component {0} exceeds {1},La quantité maximale de prestations sociales du composant {0} dépasse {1}, Maximum benefit amount of employee {0} exceeds {1},Le montant maximal des prestations sociales de l'employé {0} dépasse {1}, -Maximum discount for Item {0} is {1}%,La remise maximale pour l'article {0} est {1}%, +Maximum discount for Item {0} is {1}%,La remise maximale pour l'article {0} est {1}%, Maximum leave allowed in the leave type {0} is {1},La durée maximale autorisée pour le type de congé {0} est {1}, Medical,Médical, Medical Code,Code médical, @@ -1616,9 +1616,9 @@ Member ID,ID du membre, Member Name,Nom de membre, Member information.,Informations sur le membre, Membership,Adhésion, -Membership Details,Détails de l'adhésion, -Membership ID,ID d'adhésion, -Membership Type,Type d'adhésion, +Membership Details,Détails de l'adhésion, +Membership ID,ID d'adhésion, +Membership Type,Type d'adhésion, Memebership Details,Détails de l'adhésion, Memebership Type Details,Détails du type d'adhésion, Merge,Fusionner, @@ -1636,8 +1636,8 @@ Min Qty can not be greater than Max Qty,Qté Min ne peut pas être supérieure Minimum Lead Age (Days),Âge Minimum du Prospect (Jours), Miscellaneous Expenses,Charges Diverses, Missing Currency Exchange Rates for {0},Taux de Change Manquant pour {0}, -Missing email template for dispatch. Please set one in Delivery Settings.,Modèle de courrier électronique manquant pour l'envoi. Veuillez en définir un dans les paramètres de livraison., -"Missing value for Password, API Key or Shopify URL","Valeur manquante pour le mot de passe, la clé API ou l'URL Shopify", +Missing email template for dispatch. Please set one in Delivery Settings.,Modèle de courrier électronique manquant pour l'envoi. Veuillez en définir un dans les paramètres de livraison., +"Missing value for Password, API Key or Shopify URL","Valeur manquante pour le mot de passe, la clé API ou l'URL Shopify", Mode of Payment,Moyen de paiement, Mode of Payments,Mode de paiement, Mode of Transport,Mode de transport, @@ -1651,7 +1651,7 @@ Monthly Distribution,Répartition Mensuelle, Monthly Repayment Amount cannot be greater than Loan Amount,Montant du Remboursement Mensuel ne peut pas être supérieur au Montant du Prêt, More,Plus, More Information,Informations Complémentaires, -More than one selection for {0} not allowed,Plus d'une sélection pour {0} non autorisée, +More than one selection for {0} not allowed,Plus d'une sélection pour {0} non autorisée, More...,Plus..., Motion Picture & Video,Cinéma & Vidéo, Move,mouvement, @@ -1719,7 +1719,7 @@ Next,Suivant, Next Contact By cannot be same as the Lead Email Address,Prochain Contact Par ne peut être identique à l’Adresse Email du Prospect, Next Contact Date cannot be in the past,La Date de Prochain Contact ne peut pas être dans le passé, Next Steps,Prochaines étapes, -No Action,Pas d'action, +No Action,Pas d'action, No Customers yet!,Pas encore de clients!, No Data,Aucune Donnée, No Delivery Note selected for Customer {},Aucun bon de livraison sélectionné pour le client {}, @@ -1737,11 +1737,11 @@ No Permission,Aucune autorisation, No Quote,Aucun Devis, No Remarks,Aucune Remarque, No Result to submit,Aucun résultat à soumettre, -No Salary Structure assigned for Employee {0} on given date {1},Aucune structure de salaire attribuée à l'employé {0} à la date donnée {1}, +No Salary Structure assigned for Employee {0} on given date {1},Aucune structure de salaire attribuée à l'employé {0} à la date donnée {1}, No Staffing Plans found for this Designation,Aucun plan de dotation trouvé pour cette désignation, No Student Groups created.,Aucun Groupe d'Étudiants créé., No Students in,Aucun étudiant dans, -No Tax Withholding data found for the current Fiscal Year.,Aucune donnée de retenue d'impôt trouvée pour l'exercice en cours., +No Tax Withholding data found for the current Fiscal Year.,Aucune donnée de retenue d'impôt trouvée pour l'exercice en cours., No Work Orders created,Aucun ordre de travail créé, No accounting entries for the following warehouses,Pas d’écritures comptables pour les entrepôts suivants, No active or default Salary Structure found for employee {0} for the given dates,Aucune Structure de Salaire active ou par défaut trouvée pour employé {0} pour les dates données, @@ -1753,12 +1753,12 @@ No description given,Aucune Description, No employees for the mentioned criteria,Aucun employé pour les critères mentionnés, No gain or loss in the exchange rate,Aucun gain ou perte dans le taux de change, No items listed,Aucun article référencé, -No items to be received are overdue,Aucun article à recevoir n'est en retard, +No items to be received are overdue,Aucun article à recevoir n'est en retard, No material request created,Aucune demande de matériel créée, No more updates,Pas de mise à jour supplémentaire, -No of Interactions,Nombre d'interactions, -No of Shares,Nombre d'actions, -No pending Material Requests found to link for the given items.,Aucune demande de matériel en attente n'a été trouvée pour créer un lien vers les articles donnés., +No of Interactions,Nombre d'interactions, +No of Shares,Nombre d'actions, +No pending Material Requests found to link for the given items.,Aucune demande de matériel en attente n'a été trouvée pour créer un lien vers les articles donnés., No products found,Aucun produit trouvé, No products found.,Aucun produit trouvé., No record found,Aucun Enregistrement Trouvé, @@ -1769,7 +1769,7 @@ No salary slip found to submit for the above selected criteria OR salary slip al No tasks,Aucune tâche, No time sheets,Aucunes feuilles de temps, No values,Pas de valeurs, -No {0} found for Inter Company Transactions.,Aucun {0} n'a été trouvé pour les transactions inter-sociétés., +No {0} found for Inter Company Transactions.,Aucun {0} n'a été trouvé pour les transactions inter-sociétés., Non GST Inward Supplies,Fournitures entrantes non liées à la TPS, Non Profit,À But Non Lucratif, Non Profit (beta),Association (bêta), @@ -1792,7 +1792,7 @@ Not eligible for the admission in this program as per DOB,Non admissible à l'ad Not items found,Pas d'objets trouvés, Not permitted for {0},Non autorisé pour {0}, "Not permitted, configure Lab Test Template as required","Non autorisé, veuillez configurer le modèle de test de laboratoire", -Not permitted. Please disable the Service Unit Type,Pas permis. Veuillez désactiver le type d'unité de service, +Not permitted. Please disable the Service Unit Type,Pas permis. Veuillez désactiver le type d'unité de service, Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s),Remarque : Date de Référence / d’Échéance dépasse le nombre de jours de crédit client autorisé de {0} jour(s), Note: Item {0} entered multiple times,Remarque : Article {0} saisi plusieurs fois, Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified,Remarque : Écriture de Paiement ne sera pas créée car le compte 'Compte Bancaire ou de Caisse' n'a pas été spécifié, @@ -1801,7 +1801,7 @@ Note: There is not enough leave balance for Leave Type {0},Remarque : Le solde d Note: This Cost Center is a Group. Cannot make accounting entries against groups.,Remarque : Ce Centre de Coûts est un Groupe. Vous ne pouvez pas faire des écritures comptables sur des groupes., Note: {0},Note : {0}, Notes,Remarques, -Nothing is included in gross,Rien n'est inclus dans le brut, +Nothing is included in gross,Rien n'est inclus dans le brut, Nothing more to show.,Rien de plus à montrer., Nothing to change,Rien à changer, Notice Period,Période de préavis, @@ -1824,8 +1824,8 @@ Online,En ligne, Online Auctions,Enchères en ligne, Only Leave Applications with status 'Approved' and 'Rejected' can be submitted,Seules les Demandes de Congés avec le statut 'Appouvée' ou 'Rejetée' peuvent être soumises, "Only the Student Applicant with the status ""Approved"" will be selected in the table below.",Seul les candidatures étudiantes avec le statut «Approuvé» seront sélectionnées dans le tableau ci-dessous., -Only users with {0} role can register on Marketplace,Seuls les utilisateurs ayant le rôle {0} peuvent s'inscrire sur Marketplace, -Only {0} in stock for item {1},Seulement {0} en stock pour l'article {1}, +Only users with {0} role can register on Marketplace,Seuls les utilisateurs ayant le rôle {0} peuvent s'inscrire sur Marketplace, +Only {0} in stock for item {1},Seulement {0} en stock pour l'article {1}, Open BOM {0},Ouvrir LDM {0}, Open Item {0},Ouvrir l'Article {0}, Open Notifications,Notifications ouvertes, @@ -1837,13 +1837,13 @@ Opening (Dr),Ouverture (Dr), Opening Accounting Balance,Solde d'Ouverture de Comptabilité, Opening Accumulated Depreciation,Amortissement Cumulé d'Ouverture, Opening Accumulated Depreciation must be less than equal to {0},Amortissement Cumulé d'Ouverture doit être inférieur ou égal à {0}, -Opening Balance,Solde d'ouverture, +Opening Balance,Solde d'ouverture, Opening Balance Equity,Ouverture de la Balance des Capitaux Propres, Opening Date and Closing Date should be within same Fiscal Year,Date d'Ouverture et Date de Clôture devraient être dans le même Exercice, Opening Date should be before Closing Date,Date d'Ouverture devrait être antérieure à la Date de Clôture, Opening Entry Journal,Ecriture de journal d'ouverture, -Opening Invoice Creation Tool,Ouverture de l'outil de création de facture, -Opening Invoice Item,Ouverture d'un poste de facture, +Opening Invoice Creation Tool,Ouverture de l'outil de création de facture, +Opening Invoice Item,Ouverture d'un poste de facture, Opening Invoices,Ouverture des factures, Opening Invoices Summary,Ouverture des factures Résumé, Opening Qty,Quantité d'Ouverture, @@ -1861,7 +1861,7 @@ Opp/Lead %,Opp / Prospect %, Opportunities,Opportunités, Opportunities by lead source,Opportunités par source de plomb, Opportunity,Opportunité, -Opportunity Amount,Montant de l'opportunité, +Opportunity Amount,Montant de l'opportunité, Optional Holiday List not set for leave period {0},Une liste de vacances facultative n'est pas définie pour la période de congé {0}, "Optional. Sets company's default currency, if not specified.","Optionnel. Défini la devise par défaut de l'entreprise, si non spécifié.", Optional. This setting will be used to filter in various transactions.,Facultatif. Ce paramètre sera utilisé pour filtrer différentes transactions., @@ -1925,7 +1925,7 @@ Party Type and Party is mandatory for {0} account,Le type de tiers et le tiers s Party Type is mandatory,Type de Tiers Obligatoire, Party is mandatory,Le Tiers est obligatoire, Password,Mot de passe, -Password policy for Salary Slips is not set,La politique de mot de passe pour les bulletins de salaire n'est pas définie, +Password policy for Salary Slips is not set,La politique de mot de passe pour les bulletins de salaire n'est pas définie, Past Due Date,Date d'échéance dépassée, Patient,Patient, Patient Appointment,Rendez-vous patient, @@ -1971,7 +1971,7 @@ Payments,Paiements, Payroll,Paie, Payroll Number,Numéro de paie, Payroll Payable,Paie à Payer, -Payroll date can not be less than employee's joining date,La date de paie ne peut être inférieure à la date d'adhésion de l'employé, +Payroll date can not be less than employee's joining date,La date de paie ne peut être inférieure à la date d'adhésion de l'employé, Payslip,Fiche de paie, Pending Activities,Activités en attente, Pending Amount,Montant en attente, @@ -1994,17 +1994,17 @@ Physician,Médecin, Piecework,Travail à la pièce, Pin Code,Code PIN, Pincode,Code Postal, -Place Of Supply (State/UT),Lieu d'approvisionnement (State / UT), +Place Of Supply (State/UT),Lieu d'approvisionnement (State / UT), Place Order,Passer la commande, Plan Name,Nom du plan, Plan for maintenance visits.,Plan pour les visites de maintenance., Planned Qty,Qté Planifiée, -"Planned Qty: Quantity, for which, Work Order has been raised, but is pending to be manufactured.",Qté prévue: quantité pour laquelle l'ordre de travail a été créé mais sa fabrication est en attente., +"Planned Qty: Quantity, for which, Work Order has been raised, but is pending to be manufactured.",Qté prévue: quantité pour laquelle l'ordre de travail a été créé mais sa fabrication est en attente., Planning,Planification, Plants and Machineries,Usines et Machines, Please Set Supplier Group in Buying Settings.,Veuillez définir un groupe de fournisseurs par défaut dans les paramètres d'achat., -Please add a Temporary Opening account in Chart of Accounts,Veuillez ajouter un compte d'ouverture temporaire dans le plan comptable, -Please add the account to root level Company - ,S'il vous plaît ajouter le compte au niveau racine Société -, +Please add a Temporary Opening account in Chart of Accounts,Veuillez ajouter un compte d'ouverture temporaire dans le plan comptable, +Please add the account to root level Company - ,S'il vous plaît ajouter le compte au niveau racine Société -, Please add the remaining benefits {0} to any of the existing component,Veuillez ajouter les prestations restantes {0} à l'un des composants existants, Please check Multi Currency option to allow accounts with other currency,Veuillez vérifier l'option Multi-Devises pour permettre les comptes avec une autre devise, Please click on 'Generate Schedule',"Veuillez cliquer sur ""Générer calendrier''", @@ -2013,7 +2013,7 @@ Please click on 'Generate Schedule' to get schedule,Veuillez cliquer sur ‘Gén Please confirm once you have completed your training,Veuillez confirmer une fois que vous avez terminé votre formation, Please contact to the user who have Sales Master Manager {0} role,Veuillez contactez l'utilisateur qui a le rôle de Directeur des Ventes {0}, Please create Customer from Lead {0},Veuillez créer un client à partir du prospect {0}, -Please create purchase receipt or purchase invoice for the item {0},Veuillez créer un reçu d'achat ou une facture d'achat pour l'article {0}, +Please create purchase receipt or purchase invoice for the item {0},Veuillez créer un reçu d'achat ou une facture d'achat pour l'article {0}, Please define grade for Threshold 0%,Veuillez définir une note pour le Seuil 0%, Please enable Applicable on Booking Actual Expenses,Veuillez activer l'option : Applicable sur la base de l'enregistrement des dépenses réelles, Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses,Veuillez activer les options : Applicable sur la base des bons de commande d'achat et Applicable sur la base des bons de commande d'achat, @@ -2056,11 +2056,11 @@ Please enter repayment Amount,Veuillez entrer le Montant de remboursement, Please enter valid Financial Year Start and End Dates,Veuillez entrer des Dates de Début et de Fin d’Exercice Comptable valides, Please enter valid email address,Entrez une adresse email valide, Please enter {0} first,Veuillez d’abord entrer {0}, -Please fill in all the details to generate Assessment Result.,Veuillez renseigner tous les détails pour générer le résultat de l'évaluation., +Please fill in all the details to generate Assessment Result.,Veuillez renseigner tous les détails pour générer le résultat de l'évaluation., Please identify/create Account (Group) for type - {0},Veuillez identifier / créer un compte (groupe) pour le type - {0}, Please identify/create Account (Ledger) for type - {0},Veuillez identifier / créer un compte (grand livre) pour le type - {0}, Please input all required Result Value(s),Veuillez entrer toutes les valeurs de résultat requises, -Please login as another user to register on Marketplace,Veuillez vous connecter en tant qu'autre utilisateur pour vous inscrire sur Marketplace, +Please login as another user to register on Marketplace,Veuillez vous connecter en tant qu'autre utilisateur pour vous inscrire sur Marketplace, Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone.,Veuillez vous assurer que vous voulez vraiment supprimer tous les transactions de cette société. Vos données de base resteront intactes. Cette action ne peut être annulée., Please mention Basic and HRA component in Company,Veuillez mentionner les composants Basic et HRA dans la société, Please mention Round Off Account in Company,Veuillez indiquer le Compte d’Arrondi de la Société, @@ -2085,10 +2085,10 @@ Please select Company and Designation,Veuillez sélectionner la société et la Please select Company and Party Type first,Veuillez d’abord sélectionner une Société et le Type de Tiers, Please select Company and Posting Date to getting entries,Veuillez sélectionner la société et la date de comptabilisation pour obtenir les écritures, Please select Company first,Veuillez d’abord sélectionner une Société, -Please select Completion Date for Completed Asset Maintenance Log,Veuillez sélectionner la date d'achèvement pour le journal de maintenance des actifs terminé, -Please select Completion Date for Completed Repair,Veuillez sélectionner la date d'achèvement pour la réparation terminée, +Please select Completion Date for Completed Asset Maintenance Log,Veuillez sélectionner la date d'achèvement pour le journal de maintenance des actifs terminé, +Please select Completion Date for Completed Repair,Veuillez sélectionner la date d'achèvement pour la réparation terminée, Please select Course,Veuillez sélectionner un cours, -Please select Drug,S'il vous plaît sélectionnez Drug, +Please select Drug,S'il vous plaît sélectionnez Drug, Please select Employee,Veuillez sélectionner un employé, Please select Employee Record first.,Veuillez d’abord sélectionner le Dossier de l'Employé., Please select Existing Company for creating Chart of Accounts,Veuillez sélectionner une Société Existante pour créer un Plan de Compte, @@ -2102,7 +2102,7 @@ Please select Posting Date before selecting Party,Veuillez sélectionner la Date Please select Posting Date first,Veuillez d’abord sélectionner la Date de Comptabilisation, Please select Price List,Veuillez sélectionner une Liste de Prix, Please select Program,Veuillez sélectionner un programme, -Please select Qty against item {0},Veuillez sélectionner Qté par rapport à l'élément {0}, +Please select Qty against item {0},Veuillez sélectionner Qté par rapport à l'élément {0}, Please select Sample Retention Warehouse in Stock Settings first,Veuillez d'abord définir un entrepôt de stockage des échantillons dans les paramètres de stock, Please select Start Date and End Date for Item {0},Veuillez sélectionner la Date de Début et Date de Fin pour l'Article {0}, Please select Student Admission which is mandatory for the paid student applicant,Veuillez sélectionner obligatoirement une Admission d'Étudiant pour la candidature étudiante payée, @@ -2111,7 +2111,7 @@ Please select a Batch for Item {0}. Unable to find a single batch that fulfills Please select a Company,Veuillez sélectionner une Société, Please select a batch,Veuillez sélectionner un lot, Please select a csv file,Veuillez sélectionner un fichier csv, -Please select a customer,S'il vous plaît sélectionner un client, +Please select a customer,S'il vous plaît sélectionner un client, Please select a field to edit from numpad,Veuillez sélectionner un champ à modifier sur le pavé numérique, Please select a table,Veuillez sélectionner une table, Please select a valid Date,Veuillez sélectionner une date valide, @@ -2127,7 +2127,7 @@ Please select month and year,Veuillez sélectionner le mois et l'année, Please select prefix first,Veuillez d’abord sélectionner un préfixe, Please select the Company,Veuillez sélectionner la société, Please select the Company first,Veuillez sélectionner la Société en premier, -Please select the Multiple Tier Program type for more than one collection rules.,Veuillez sélectionner le type de programme à plusieurs niveaux pour plus d'une règle de collecte., +Please select the Multiple Tier Program type for more than one collection rules.,Veuillez sélectionner le type de programme à plusieurs niveaux pour plus d'une règle de collecte., Please select the assessment group other than 'All Assessment Groups',Sélectionnez un groupe d'évaluation autre que «Tous les Groupes d'Évaluation», Please select the document type first,Veuillez d’abord sélectionner le type de document, Please select weekly off day,Veuillez sélectionnez les jours de congé hebdomadaires, @@ -2149,9 +2149,9 @@ Please set Number of Depreciations Booked,Veuillez définir le Nombre d’Amorti Please set Unrealized Exchange Gain/Loss Account in Company {0},Veuillez définir un compte de gain / perte de change non réalisé pour la société {0}, Please set User ID field in an Employee record to set Employee Role,Veuillez définir le champ ID de l'Utilisateur dans un dossier Employé pour définir le Rôle de l’Employés, Please set a default Holiday List for Employee {0} or Company {1},Veuillez définir une Liste de Vacances par défaut pour l'Employé {0} ou la Société {1}, -Please set account in Warehouse {0},Veuillez définir un compte dans l'entrepôt {0}, +Please set account in Warehouse {0},Veuillez définir un compte dans l'entrepôt {0}, Please set an active menu for Restaurant {0},Veuillez définir un menu actif pour le restaurant {0}, -Please set associated account in Tax Withholding Category {0} against Company {1},Veuillez définir le compte associé dans la catégorie de retenue d'impôt {0} contre la société {1}., +Please set associated account in Tax Withholding Category {0} against Company {1},Veuillez définir le compte associé dans la catégorie de retenue d'impôt {0} contre la société {1}., Please set at least one row in the Taxes and Charges Table,Veuillez définir au moins une ligne dans le tableau des taxes et des frais., Please set default Cash or Bank account in Mode of Payment {0},Veuillez définir un compte de Caisse ou de Banque par défaut pour le Mode de Paiement {0}, Please set default account in Salary Component {0},Veuillez définir le compte par défaut dans la Composante Salariale {0}, @@ -2161,17 +2161,17 @@ Please set default template for Leave Approval Notification in HR Settings.,Veui Please set default template for Leave Status Notification in HR Settings.,Veuillez définir un modèle par défaut pour la notification de statut de congés dans les paramètres RH., Please set default {0} in Company {1},Veuillez définir {0} par défaut dans la Société {1}, Please set filter based on Item or Warehouse,Veuillez définir un filtre basé sur l'Article ou l'Entrepôt, -Please set leave policy for employee {0} in Employee / Grade record,Veuillez définir la politique de congé pour l'employé {0} dans le dossier Employé / Grade, +Please set leave policy for employee {0} in Employee / Grade record,Veuillez définir la politique de congé pour l'employé {0} dans le dossier Employé / Grade, Please set recurring after saving,Veuillez définir la récurrence après avoir sauvegardé, Please set the Company,Veuillez définir la Société, -Please set the Customer Address,Veuillez définir l'adresse du client, +Please set the Customer Address,Veuillez définir l'adresse du client, Please set the Date Of Joining for employee {0},Veuillez définir la Date d'Embauche pour l'employé {0}, Please set the Default Cost Center in {0} company.,Veuillez définir un centre de coûts par défaut pour la société {0}., Please set the Email ID for the Student to send the Payment Request,Configurez l'ID de courrier électronique pour que l'Élève envoie la Demande de Paiement, Please set the Item Code first,Veuillez définir le Code d'Article en premier, Please set the Payment Schedule,Veuillez définir le calendrier de paiement, Please set the series to be used.,Veuillez définir la série à utiliser., -Please set {0} for address {1},Définissez {0} pour l'adresse {1}., +Please set {0} for address {1},Définissez {0} pour l'adresse {1}., Please setup Students under Student Groups,Veuillez configurer les Étudiants sous des groupes d'Étudiants, Please share your feedback to the training by clicking on 'Training Feedback' and then 'New',"Partagez vos commentaires sur la formation en cliquant sur 'Retour d'Expérience de la formation', puis 'Nouveau'", Please specify Company,Veuillez spécifier la Société, @@ -2227,13 +2227,13 @@ Pricing Rule,Règle de tarification, Pricing Rule {0} is updated,La règle de tarification {0} est mise à jour, Pricing Rules are further filtered based on quantity.,Les Règles de Tarification sont d'avantage filtrés en fonction de la quantité., Primary,Primaire, -Primary Address Details,Détails de l'adresse principale, +Primary Address Details,Détails de l'adresse principale, Primary Contact Details,Détails du contact principal, Principal Amount,Montant Principal, -Print Format,Format d'impression, +Print Format,Format d'impression, Print IRS 1099 Forms,Imprimer les formulaires IRS 1099, Print Report Card,Imprimer le rapport, -Print Settings,Paramètres d'impression, +Print Settings,Paramètres d'impression, Print and Stationery,Impression et Papeterie, Print settings updated in respective print format,Paramètres d'impression mis à jour avec le format d'impression indiqué, Print taxes with zero amount,Impression de taxes avec un montant nul, @@ -2261,7 +2261,7 @@ Profit and Loss,Pertes et Profits, Profit for the year,Bénéfice de l'exercice, Program,Programme, Program in the Fee Structure and Student Group {0} are different.,Le programme dans la structure d'honoraires et le groupe d'étudiants {0} sont différents., -Program {0} does not exist.,Le programme {0} n'existe pas., +Program {0} does not exist.,Le programme {0} n'existe pas., Program: ,Programme:, Progress % for a task cannot be more than 100.,% de Progression pour une tâche ne peut pas être supérieur à 100., Project Collaboration Invitation,Invitation de Collaboration à un Projet, @@ -2301,7 +2301,7 @@ Purchase Order,Bon de commande, Purchase Order Amount,Bon de commande, Purchase Order Amount(Company Currency),Montant du bon de commande (devise de la société), Purchase Order Date,Date du bon de commande, -Purchase Order Items not received on time,Articles de commande d'achat non reçus à temps, +Purchase Order Items not received on time,Articles de commande d'achat non reçus à temps, Purchase Order number required for Item {0},Numéro de Bon de Commande requis pour l'Article {0}, Purchase Order to Payment,Du Bon de Commande au Paiement, Purchase Order {0} is not submitted,Le Bon de Commande {0} n’est pas soumis, @@ -2324,7 +2324,7 @@ Quality,Qualité, Quality Action,Action Qualité, Quality Goal.,Objectif de qualité., Quality Inspection,Inspection de la Qualité, -Quality Inspection: {0} is not submitted for the item: {1} in row {2},Contrôle qualité: {0} n'est pas soumis pour l'élément: {1} à la ligne {2}., +Quality Inspection: {0} is not submitted for the item: {1} in row {2},Contrôle qualité: {0} n'est pas soumis pour l'élément: {1} à la ligne {2}., Quality Management,Gestion de la qualité, Quality Meeting,Réunion de qualité, Quality Procedure,Procédure de qualité, @@ -2371,12 +2371,12 @@ Reading Uploaded File,Lecture du fichier téléchargé, Real Estate,Immobilier, Reason For Putting On Hold,Raison de la mise en attente, Reason for Hold,Raison de tenir, -Reason for hold: ,Raison de l'attente:, +Reason for hold: ,Raison de l'attente:, Receipt,Reçu, Receipt document must be submitted,Le reçu doit être soumis, Receivable,Créance, Receivable Account,Compte Débiteur, -Receive at Warehouse Entry,Recevoir à l'entrée de l'entrepôt, +Receive at Warehouse Entry,Recevoir à l'entrée de l'entrepôt, Received,Reçu, Received On,Reçu le, Received Quantity,Quantité reçue, @@ -2402,7 +2402,7 @@ Reference No.,Numéro de référence, Reference Number,Numéro de réference, Reference Owner,Responsable de la Référence, Reference Type,Type de référence, -"Reference: {0}, Item Code: {1} and Customer: {2}","Référence: {0}, Code de l'article: {1} et Client: {2}", +"Reference: {0}, Item Code: {1} and Customer: {2}","Référence: {0}, Code de l'article: {1} et Client: {2}", References,Références, Refresh Token,Jeton de Rafraîchissement, Region,Région, @@ -2413,7 +2413,7 @@ Related,en relation, Relation with Guardian1,Relation avec Tuteur1, Relation with Guardian2,Relation avec Tuteur2, Release Date,Date de la fin de mise en attente, -Reload Linked Analysis,Recharger l'analyse liée, +Reload Linked Analysis,Recharger l'analyse liée, Remaining,Restant, Remaining Balance,Solde restant, Remarks,Remarques, @@ -2460,16 +2460,16 @@ Reserved Qty,Qté Réservées, Reserved Qty for Production,Qté Réservée pour la Production, Reserved Qty for Production: Raw materials quantity to make manufacturing items.,Qté réservée à la production: quantité de matières premières permettant de fabriquer des articles de fabrication., "Reserved Qty: Quantity ordered for sale, but not delivered.","Réservés Quantité: Quantité de commande pour la vente , mais pas livré .", -Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied,L'entrepôt réservé est obligatoire pour l'article {0} dans les matières premières fournies, +Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied,L'entrepôt réservé est obligatoire pour l'article {0} dans les matières premières fournies, Reserved for manufacturing,Réservé pour la production, Reserved for sale,Réservé à la vente, Reserved for sub contracting,Réservé à la sous-traitance, Resistant,Résistant, -Resolve error and upload again.,Résoudre l'erreur et télécharger à nouveau., +Resolve error and upload again.,Résoudre l'erreur et télécharger à nouveau., Response,Réponse, Responsibilities,Responsabilités, Rest Of The World,Reste du monde, -Restart Subscription,Redémarrer l'abonnement, +Restart Subscription,Redémarrer l'abonnement, Restaurant,Restaurant, Result Date,Date de résultat, Result already Submitted,Résultat déjà soumis, @@ -2479,13 +2479,13 @@ Retail & Wholesale,Vente de Détail & en Gros, Retail Operations,Opérations de détail, Retained Earnings,Bénéfices Non Répartis, Retention Stock Entry,Entrée de stock de rétention, -Retention Stock Entry already created or Sample Quantity not provided,Saisie de stock de rétention déjà créée ou quantité d'échantillon non fournie, +Retention Stock Entry already created or Sample Quantity not provided,Saisie de stock de rétention déjà créée ou quantité d'échantillon non fournie, Return,Retour, Return / Credit Note,Retour / Note de crédit, Return / Debit Note,Retour / Note de Débit, Returns,Retours, Reverse Journal Entry,Ecriture de journal de contre-passation, -Review Invitation Sent,Examiner l'invitation envoyée, +Review Invitation Sent,Examiner l'invitation envoyée, Review and Action,Révision et action, Role,Rôle, Rooms Booked,Chambres réservées, @@ -2506,10 +2506,10 @@ Row # {0}: Serial No is mandatory,Ligne # {0} : N° de série est obligatoire, Row # {0}: Serial No {1} does not match with {2} {3},Ligne # {0} : N° de série {1} ne correspond pas à {2} {3}, Row #{0} (Payment Table): Amount must be negative,Row # {0} (Table de paiement): le montant doit être négatif, Row #{0} (Payment Table): Amount must be positive,Ligne #{0} (Table de paiement): Le montant doit être positif, -Row #{0}: Account {1} does not belong to company {2},Ligne # {0}: le compte {1} n'appartient pas à la société {2}, +Row #{0}: Account {1} does not belong to company {2},Ligne # {0}: le compte {1} n'appartient pas à la société {2}, Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Ligne # {0}: montant attribué ne peut pas être supérieur au montant en souffrance., "Row #{0}: Asset {1} cannot be submitted, it is already {2}","Ligne #{0} : L’Actif {1} ne peut pas être soumis, il est déjà {2}", -Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,Ligne n ° {0}: impossible de définir le tarif si le montant est supérieur au montant facturé pour l'élément {1}., +Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,Ligne n ° {0}: impossible de définir le tarif si le montant est supérieur au montant facturé pour l'élément {1}., Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Ligne #{0} : Date de compensation {1} ne peut pas être antérieure à la Date du Chèque {2}, Row #{0}: Duplicate entry in References {1} {2},Ligne # {0}: entrée en double dans les références {1} {2}, Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Ligne {0}: la date de livraison prévue ne peut pas être avant la date de commande, @@ -2527,15 +2527,15 @@ Row #{0}: Rejected Qty can not be entered in Purchase Return,Ligne #{0} : Qté R Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Ligne #{0} : Entrepôt de Rejet est obligatoire pour l’Article rejeté {1}, Row #{0}: Reqd by Date cannot be before Transaction Date,La ligne # {0}: Reqd par date ne peut pas être antérieure à la date de la transaction, Row #{0}: Set Supplier for item {1},Ligne #{0} : Définir Fournisseur pour l’article {1}, -Row #{0}: Status must be {1} for Invoice Discounting {2},Ligne n ° {0}: l'état doit être {1} pour l'actualisation de facture {2}., +Row #{0}: Status must be {1} for Invoice Discounting {2},Ligne n ° {0}: l'état doit être {1} pour l'actualisation de facture {2}., "Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Ligne # {0}: Le lot {1} n'a que {2} qté(s). Veuillez sélectionner un autre lot contenant {3} qtés disponible ou diviser la rangée en plusieurs lignes, pour livrer / émettre à partir de plusieurs lots", Row #{0}: Timings conflicts with row {1},Ligne #{0}: Minutage en conflit avec la ligne {1}, Row #{0}: {1} can not be negative for item {2},Ligne #{0} : {1} ne peut pas être négatif pour l’article {2}, Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Ligne N° {0}: Le montant ne peut être supérieur au Montant en Attente pour la Note de Frais {1}. Le Montant en Attente est de {2}, -Row {0} : Operation is required against the raw material item {1},Ligne {0}: l'opération est requise pour l'article de matière première {1}, +Row {0} : Operation is required against the raw material item {1},Ligne {0}: l'opération est requise pour l'article de matière première {1}, Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2},La ligne {0} # Montant alloué {1} ne peut pas être supérieure au montant non réclamé {2}, -Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},La ligne {0} # article {1} ne peut pas être transférée plus de {2} par commande d'achat {3}, -Row {0}# Paid Amount cannot be greater than requested advance amount,La ligne {0} # Montant payé ne peut pas être supérieure au montant de l'avance demandée, +Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},La ligne {0} # article {1} ne peut pas être transférée plus de {2} par commande d'achat {3}, +Row {0}# Paid Amount cannot be greater than requested advance amount,La ligne {0} # Montant payé ne peut pas être supérieure au montant de l'avance demandée, Row {0}: Activity Type is mandatory.,Ligne {0} : Le Type d'Activité est obligatoire., Row {0}: Advance against Customer must be credit,Ligne {0} : L’Avance du Client doit être un crédit, Row {0}: Advance against Supplier must be debit,Ligne {0} : L’Avance du Fournisseur doit être un débit, @@ -2548,7 +2548,7 @@ Row {0}: Cost center is required for an item {1},Ligne {0}: le Centre de Coûts Row {0}: Credit entry can not be linked with a {1},Ligne {0} : L’Écriture de crédit ne peut pas être liée à un {1}, Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Ligne {0} : La devise de la LDM #{1} doit être égale à la devise sélectionnée {2}, Row {0}: Debit entry can not be linked with a {1},Ligne {0} : L’Écriture de Débit ne peut pas être lié à un {1}, -Row {0}: Depreciation Start Date is required,Ligne {0}: la date de début de l'amortissement est obligatoire, +Row {0}: Depreciation Start Date is required,Ligne {0}: la date de début de l'amortissement est obligatoire, Row {0}: Enter location for the asset item {1},Ligne {0}: entrez la localisation de l'actif {1}, Row {0}: Exchange Rate is mandatory,Ligne {0} : Le Taux de Change est obligatoire, Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount,Ligne {0}: la valeur attendue après la durée de vie utile doit être inférieure au montant brut de l'achat, @@ -2562,19 +2562,19 @@ Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Ligne {0} : Ti Row {0}: Party Type and Party is required for Receivable / Payable account {1},Ligne {0} : Le Type de Tiers et le Tiers sont requis pour le compte Débiteur / Créditeur {1}, Row {0}: Payment against Sales/Purchase Order should always be marked as advance,Ligne {0} : Paiements contre Commandes Client / Fournisseur doivent toujours être marqués comme des avances, Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.,Ligne {0} : Veuillez vérifier 'Est Avance' sur le compte {1} si c'est une avance., -Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges,Ligne {0}: Définissez le motif d'exemption de taxe dans les taxes de vente et les frais., +Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges,Ligne {0}: Définissez le motif d'exemption de taxe dans les taxes de vente et les frais., Row {0}: Please set the Mode of Payment in Payment Schedule,Ligne {0}: Veuillez définir le mode de paiement dans le calendrier de paiement., Row {0}: Please set the correct code on Mode of Payment {1},Ligne {0}: définissez le code correct sur le mode de paiement {1}., Row {0}: Qty is mandatory,Ligne {0} : Qté obligatoire, -Row {0}: Quality Inspection rejected for item {1},Ligne {0}: le contrôle qualité a été rejeté pour l'élément {1}., +Row {0}: Quality Inspection rejected for item {1},Ligne {0}: le contrôle qualité a été rejeté pour l'élément {1}., Row {0}: UOM Conversion Factor is mandatory,Ligne {0} : Facteur de Conversion LDM est obligatoire, -Row {0}: select the workstation against the operation {1},Ligne {0}: sélectionnez le poste de travail en fonction de l'opération {1}, +Row {0}: select the workstation against the operation {1},Ligne {0}: sélectionnez le poste de travail en fonction de l'opération {1}, Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.,Ligne {0}: {1} Numéros de série requis pour l'article {2}. Vous en avez fourni {3}., -Row {0}: {1} is required to create the Opening {2} Invoices,La ligne {0}: {1} est requise pour créer les factures d'ouverture {2}, +Row {0}: {1} is required to create the Opening {2} Invoices,La ligne {0}: {1} est requise pour créer les factures d'ouverture {2}, Row {0}: {1} must be greater than 0,Ligne {0}: {1} doit être supérieure à 0, Row {0}: {1} {2} does not match with {3},Ligne {0} : {1} {2} ne correspond pas à {3}, Row {0}:Start Date must be before End Date,Ligne {0} : La Date de Début doit être avant la Date de Fin, -Rows with duplicate due dates in other rows were found: {0},Des lignes avec des dates d'échéance en double dans les autres lignes ont été trouvées: {0}, +Rows with duplicate due dates in other rows were found: {0},Des lignes avec des dates d'échéance en double dans les autres lignes ont été trouvées: {0}, Rules for adding shipping costs.,Règles pour l'ajout de frais de port., Rules for applying pricing and discount.,Règles pour l’application des tarifs et des remises., S.O. No.,S.O. N°., @@ -2586,10 +2586,10 @@ Salary Slip ID,ID Fiche de Paie, Salary Slip of employee {0} already created for this period,Fiche de Paie de l'employé {0} déjà créée pour cette période, Salary Slip of employee {0} already created for time sheet {1},Fiche de Paie de l'employé {0} déjà créée pour la feuille de temps {1}, Salary Slip submitted for period from {0} to {1},Fiche de paie soumise pour la période du {0} au {1}, -Salary Structure Assignment for Employee already exists,La structure de la structure salariale pour l'employé existe déjà, +Salary Structure Assignment for Employee already exists,La structure de la structure salariale pour l'employé existe déjà, Salary Structure Missing,Grille des Salaires Manquante, -Salary Structure must be submitted before submission of Tax Ememption Declaration,La structure salariale doit être soumise avant la soumission de la déclaration d'émigration fiscale, -Salary Structure not found for employee {0} and date {1},Structure de salaire non trouvée pour l'employé {0} et la date {1}, +Salary Structure must be submitted before submission of Tax Ememption Declaration,La structure salariale doit être soumise avant la soumission de la déclaration d'émigration fiscale, +Salary Structure not found for employee {0} and date {1},Structure de salaire non trouvée pour l'employé {0} et la date {1}, Salary Structure should have flexible benefit component(s) to dispense benefit amount,La structure salariale devrait comporter une ou plusieurs composantes de prestation sociales variables pour la distribution du montant de la prestation, "Salary already processed for period between {0} and {1}, Leave application period cannot be between this date range.","Salaire déjà traité pour la période entre {0} et {1}, La période de demande de congé ne peut pas être entre cette plage de dates.", Sales,Ventes, @@ -2626,7 +2626,7 @@ Same item cannot be entered multiple times.,Le même article ne peut pas être e Same supplier has been entered multiple times,Le même fournisseur a été saisi plusieurs fois, Sample,Échantillon, Sample Collection,Collecte d'Échantillons, -Sample quantity {0} cannot be more than received quantity {1},La quantité d'échantillon {0} ne peut pas dépasser la quantité reçue {1}, +Sample quantity {0} cannot be more than received quantity {1},La quantité d'échantillon {0} ne peut pas dépasser la quantité reçue {1}, Sanctioned,Sanctionné, Sanctioned Amount,Montant Approuvé, Sanctioned Amount cannot be greater than Claim Amount in Row {0}.,Le Montant Approuvé ne peut pas être supérieur au Montant Réclamé à la ligne {0}., @@ -2636,12 +2636,12 @@ Saved,Enregistré, Saving {0},Enregistrement {0}, Scan Barcode,Scan Code Barre, Schedule,Calendrier, -Schedule Admission,Calendrier d'admission, +Schedule Admission,Calendrier d'admission, Schedule Course,Cours Calendrier, Schedule Date,Date du Calendrier, Schedule Discharge,Décharge horaire, Scheduled,Prévu, -Scheduled Upto,Programmé jusqu'à, +Scheduled Upto,Programmé jusqu'à, "Schedules for {0} overlaps, do you want to proceed after skiping overlaped slots ?","Les plannings pour {0} se chevauchent, voulez-vous continuer sans prendre en compte les créneaux qui se chevauchent ?", Score cannot be greater than Maximum Score,Score ne peut pas être supérieure à Score maximum, Score must be less than or equal to 5,Score doit être inférieur ou égal à 5, @@ -2666,7 +2666,7 @@ See past orders,Voir les commandes passées, See past quotations,Voir les citations passées, Select,Sélectionner, Select Alternate Item,Sélectionnez un autre élément, -Select Attribute Values,Sélectionner les valeurs d'attribut, +Select Attribute Values,Sélectionner les valeurs d'attribut, Select BOM,Sélectionner LDM, Select BOM and Qty for Production,Sélectionner la LDM et la Qté pour la Production, "Select BOM, Qty and For Warehouse","Sélectionner une nomenclature, une quantité et un entrepôt", @@ -2703,7 +2703,7 @@ Select or add new customer,Sélectionner ou ajoutez nouveau client, Select students manually for the Activity based Group,Sélectionner les élèves manuellement pour un Groupe basé sur l'Activité, Select the customer or supplier.,Veuillez sélectionner le client ou le fournisseur., Select the nature of your business.,Sélectionner la nature de votre entreprise., -Select the program first,Sélectionnez d'abord le programme, +Select the program first,Sélectionnez d'abord le programme, Select to add Serial Number.,Sélectionnez pour ajouter un numéro de série., Select your Domains,Sélectionnez vos domaines, Selected Price List should have buying and selling fields checked.,La liste de prix sélectionnée doit avoir les champs d'achat et de vente cochés., @@ -2724,7 +2724,7 @@ Sent,Envoyé, Serial #,Série #, Serial No and Batch,N° de Série et lot, Serial No is mandatory for Item {0},N° de Série est obligatoire pour l'Article {0}, -Serial No {0} does not belong to Batch {1},Le numéro de série {0} n'appartient pas au lot {1}, +Serial No {0} does not belong to Batch {1},Le numéro de série {0} n'appartient pas au lot {1}, Serial No {0} does not belong to Delivery Note {1},N° de Série {0} ne fait pas partie du Bon de Livraison {1}, Serial No {0} does not belong to Item {1},N° de Série {0} n'appartient pas à l'Article {1}, Serial No {0} does not belong to Warehouse {1},N° de Série {0} ne fait pas partie de l’Entrepôt {1}, @@ -2753,11 +2753,11 @@ Service Expense,Frais de service, Service Level Agreement,Contrat de niveau de service, Service Level Agreement.,Contrat de niveau de service., Service Level.,Niveau de service., -Service Stop Date cannot be after Service End Date,La date d'arrêt du service ne peut pas être postérieure à la date de fin du service, -Service Stop Date cannot be before Service Start Date,La date d'arrêt du service ne peut pas être antérieure à la date de début du service, +Service Stop Date cannot be after Service End Date,La date d'arrêt du service ne peut pas être postérieure à la date de fin du service, +Service Stop Date cannot be before Service Start Date,La date d'arrêt du service ne peut pas être antérieure à la date de début du service, Services,Services, "Set Default Values like Company, Currency, Current Fiscal Year, etc.","Définir les Valeurs par Défaut comme : Societé, Devise, Exercice Actuel, etc...", -Set Details,Détails de l'ensemble, +Set Details,Détails de l'ensemble, Set New Release Date,Définir la nouvelle date de fin de mise en attente, Set Project and all Tasks to status {0}?,Définir le projet et toutes les tâches sur le statut {0}?, Set Status,Définir le statut, @@ -2769,15 +2769,15 @@ Set as Lost,Définir comme perdu, Set as Open,Définir comme ouvert, Set default inventory account for perpetual inventory,Configurer le compte d'inventaire par défaut pour l'inventaire perpétuel, Set default mode of payment,Définir le mode de paiement par défaut, -Set this if the customer is a Public Administration company.,Définissez cette option si le client est une société d'administration publique., -Set {0} in asset category {1} or company {2},Définissez {0} dans la catégorie d'actifs {1} ou la société {2}, +Set this if the customer is a Public Administration company.,Définissez cette option si le client est une société d'administration publique., +Set {0} in asset category {1} or company {2},Définissez {0} dans la catégorie d'actifs {1} ou la société {2}, "Setting Events to {0}, since the Employee attached to the below Sales Persons does not have a User ID{1}","Définir les Événements à {0}, puisque l'employé attaché au Commercial ci-dessous n'a pas d'ID Utilisateur {1}", Setting defaults,Définition des valeurs par défaut, Setting up Email,Configurer l'Email, Setting up Email Account,Configuration du Compte Email, Setting up Employees,Configuration des Employés, Setting up Taxes,Configuration des Impôts, -Setting up company,Création d'entreprise, +Setting up company,Création d'entreprise, Settings,Paramètres, "Settings for online shopping cart such as shipping rules, price list etc.","Paramètres du panier tels que les règles de livraison, liste de prix, etc.", Settings for website homepage,Paramètres de la page d'accueil du site, @@ -2799,9 +2799,9 @@ Ship To State,Ship To State, Shipments,Livraisons, Shipping,livraison, Shipping Address,Adresse de livraison, -"Shipping Address does not have country, which is required for this Shipping Rule","L'adresse de livraison n'a pas de pays, ce qui est requis pour cette règle d'expédition", -Shipping rule only applicable for Buying,Règle d'expédition applicable uniquement pour l'achat, -Shipping rule only applicable for Selling,Règle d'expédition applicable uniquement pour la vente, +"Shipping Address does not have country, which is required for this Shipping Rule","L'adresse de livraison n'a pas de pays, ce qui est requis pour cette règle d'expédition", +Shipping rule only applicable for Buying,Règle d'expédition applicable uniquement pour l'achat, +Shipping rule only applicable for Selling,Règle d'expédition applicable uniquement pour la vente, Shopify Supplier,Fournisseur Shopify, Shopping Cart,Panier, Shopping Cart Settings,Paramètres du panier, @@ -2809,9 +2809,9 @@ Short Name,Nom court, Shortage Qty,Qté de Pénurie, Show Completed,Montrer terminé, Show Cumulative Amount,Afficher le montant cumulatif, -Show Employee,Afficher l'employé, +Show Employee,Afficher l'employé, Show Open,Afficher ouverte, -Show Opening Entries,Afficher les entrées d'ouverture, +Show Opening Entries,Afficher les entrées d'ouverture, Show Payment Details,Afficher les détails du paiement, Show Return Entries,Afficher les entrées de retour, Show Salary Slip,Afficher la Fiche de Salaire, @@ -2827,7 +2827,7 @@ Silt,Limon, Single Variant,Variante unique, Single unit of an Item.,Seule unité d'un Article., "Skipping Leave Allocation for the following employees, as Leave Allocation records already exists against them. {0}","Attribution des congés de congé pour les employés suivants, car des dossiers de répartition des congés existent déjà contre eux. {0}", -"Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}","Ignorer l'affectation de structure salariale pour les employés suivants, car des enregistrements d'affectation de structure salariale existent déjà pour eux. {0}", +"Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}","Ignorer l'affectation de structure salariale pour les employés suivants, car des enregistrements d'affectation de structure salariale existent déjà pour eux. {0}", Slideshow,Diaporama, Slots for {0} are not added to the schedule,Les créneaux pour {0} ne sont pas ajoutés à l'agenda, Small,Petit, @@ -2860,9 +2860,9 @@ Standard Buying,Achat standard, Standard Selling,Vente standard, Standard contract terms for Sales or Purchase.,Termes contractuels standards pour Ventes ou Achats, Start Date,Date de début, -Start Date of Agreement can't be greater than or equal to End Date.,La date de début de l'accord ne peut être supérieure ou égale à la date de fin., +Start Date of Agreement can't be greater than or equal to End Date.,La date de début de l'accord ne peut être supérieure ou égale à la date de fin., Start Year,Année de début, -"Start and end dates not in a valid Payroll Period, cannot calculate {0}","Les dates de début et de fin ne faisant pas partie d'une période de paie valide, impossible de calculer {0}", +"Start and end dates not in a valid Payroll Period, cannot calculate {0}","Les dates de début et de fin ne faisant pas partie d'une période de paie valide, impossible de calculer {0}", "Start and end dates not in a valid Payroll Period, cannot calculate {0}.","Les dates de début et de fin ne figurant pas dans une période de paie valide, le système ne peut pas calculer {0}.", Start date should be less than end date for Item {0},La date de début doit être antérieure à la date de fin pour l'Article {0}, Start date should be less than end date for task {0},La date de début doit être inférieure à la date de fin de la tâche {0}, @@ -2919,17 +2919,17 @@ Student Group,Groupe Étudiant, Student Group Strength,Force du Groupe d'Étudiant, Student Group is already updated.,Le Groupe d'Étudiants est déjà mis à jour., Student Group or Course Schedule is mandatory,Le Ggroupe d'Étudiants ou le Calendrier des Cours est obligatoire, -Student Group: ,Groupe d'étudiants:, +Student Group: ,Groupe d'étudiants:, Student ID,Carte d'Étudiant, -Student ID: ,Carte d'étudiant:, +Student ID: ,Carte d'étudiant:, Student LMS Activity,Activité LMS des étudiants, Student Mobile No.,N° de Mobile de l'Étudiant, Student Name,Nom de l'Étudiant, -Student Name: ,Nom d'étudiant:, +Student Name: ,Nom d'étudiant:, Student Report Card,Carte d'étudiant, Student is already enrolled.,L'étudiant est déjà inscrit., Student {0} - {1} appears Multiple times in row {2} & {3},Étudiant {0} - {1} apparaît Plusieurs fois dans la ligne {2} & {3}, -Student {0} does not belong to group {1},L'élève {0} n'appartient pas au groupe {1}, +Student {0} does not belong to group {1},L'élève {0} n'appartient pas au groupe {1}, Student {0} exist against student applicant {1},Étudiant {0} existe pour la candidature d'un étudiant {1}, "Students are at the heart of the system, add all your students","Les étudiants sont au cœur du système, ajouter tous vos étudiants", Sub Assemblies,Sous-Ensembles, @@ -2977,10 +2977,10 @@ Supplier Warehouse mandatory for sub-contracted Purchase Receipt,Entrepôt Fourn Supplier database.,Base de données fournisseurs., Supplier {0} not found in {1},Fournisseur {0} introuvable dans {1}, Supplier(s),Fournisseur(s), -Supplies made to UIN holders,Fournitures faites aux titulaires de l'UIN, +Supplies made to UIN holders,Fournitures faites aux titulaires de l'UIN, Supplies made to Unregistered Persons,Fournitures faites à des personnes non inscrites, Suppliies made to Composition Taxable Persons,Suppleies à des personnes assujetties à la composition, -Supply Type,Type d'approvisionnement, +Supply Type,Type d'approvisionnement, Support,Soutien, Support Analytics,Analyse du Support, Support Settings,Paramètres du Support, @@ -3014,9 +3014,9 @@ Tax Rate,Taux d'Imposition, Tax Rule Conflicts with {0},Règle de Taxation est en Conflit avec {0}, Tax Rule for transactions.,Règle de Taxation pour les transactions., Tax Template is mandatory.,Un Modèle de Taxe est obligatoire., -Tax Withholding rates to be applied on transactions.,Taux de retenue d'impôt à appliquer aux transactions., +Tax Withholding rates to be applied on transactions.,Taux de retenue d'impôt à appliquer aux transactions., Tax template for buying transactions.,Modèle de taxe pour les opérations d’achat., -Tax template for item tax rates.,Modèle de taxe pour les taux de taxe d'article., +Tax template for item tax rates.,Modèle de taxe pour les taux de taxe d'article., Tax template for selling transactions.,Modèle de taxe pour les opérations de vente., Taxable Amount,Montant Taxable, Taxes,Taxes, @@ -3044,7 +3044,7 @@ Thank you for your business!,Merci pour votre entreprise !, The 'From Package No.' field must neither be empty nor it's value less than 1.,Le champ 'N° de Paquet' ne doit pas être vide ni sa valeur être inférieure à 1., The Brand,La marque, The Item {0} cannot have Batch,L'Article {0} ne peut être en Lot, -The Loyalty Program isn't valid for the selected company,Le programme de fidélité n'est pas valable pour la société sélectionnée, +The Loyalty Program isn't valid for the selected company,Le programme de fidélité n'est pas valable pour la société sélectionnée, The Payment Term at row {0} is possibly a duplicate.,Le délai de paiement à la ligne {0} est probablement un doublon., The Term End Date cannot be earlier than the Term Start Date. Please correct the dates and try again.,La Date de Fin de Terme ne peut pas être antérieure à la Date de Début de Terme. Veuillez corriger les dates et essayer à nouveau., The Term End Date cannot be later than the Year End Date of the Academic Year to which the term is linked (Academic Year {}). Please correct the dates and try again.,La Date de Fin de Terme ne peut pas être postérieure à la Date de Fin de l'Année Académique à laquelle le terme est lié (Année Académique {}). Veuillez corriger les dates et essayer à nouveau., @@ -3064,21 +3064,21 @@ The payment gateway account in plan {0} is different from the payment gateway ac The request for quotation can be accessed by clicking on the following link,La demande de devis peut être consultée en cliquant sur le lien suivant, The selected BOMs are not for the same item,Les LDMs sélectionnées ne sont pas pour le même article, The selected item cannot have Batch,L’article sélectionné ne peut pas avoir de Lot, -The seller and the buyer cannot be the same,Le vendeur et l'acheteur ne peuvent pas être les mêmes, -The shareholder does not belong to this company,L'actionnaire n'appartient pas à cette société, +The seller and the buyer cannot be the same,Le vendeur et l'acheteur ne peuvent pas être les mêmes, +The shareholder does not belong to this company,L'actionnaire n'appartient pas à cette société, The shares already exist,Les actions existent déjà, The shares don't exist with the {0},Les actions n'existent pas pour {0}, -"The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage","La tâche a été mise en file d'attente en tant que tâche en arrière-plan. En cas de problème de traitement en arrière-plan, le système ajoute un commentaire concernant l'erreur sur ce rapprochement des stocks et revient au stade de brouillon.", +"The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage","La tâche a été mise en file d'attente en tant que tâche en arrière-plan. En cas de problème de traitement en arrière-plan, le système ajoute un commentaire concernant l'erreur sur ce rapprochement des stocks et revient au stade de brouillon.", "Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc.","Les Règles de Tarification sont ensuite filtrées en fonction des Clients, des Groupes de Clients, des Régions, des Fournisseurs, des Groupes de Fournisseurs, des Campagnes, des Partenaires Commerciaux, etc.", "There are inconsistencies between the rate, no of shares and the amount calculated","Il existe des incohérences entre le prix unitaire, le nombre d'actions et le montant calculé", There are more holidays than working days this month.,Il y a plus de vacances que de jours travaillés ce mois-ci., There can be multiple tiered collection factor based on the total spent. But the conversion factor for redemption will always be same for all the tier.,Il peut y avoir plusieurs facteurs de collecte hiérarchisés en fonction du total dépensé. Mais le facteur de conversion pour l'échange sera toujours le même pour tous les niveaux., There can only be 1 Account per Company in {0} {1},Il ne peut y avoir qu’un Compte par Société dans {0} {1}, "There can only be one Shipping Rule Condition with 0 or blank value for ""To Value""","Il ne peut y avoir qu’une Condition de Règle de Livraison avec 0 ou une valeur vide pour « A la Valeur""", -There is no leave period in between {0} and {1},Il n'y a pas de période de congé entre {0} et {1}, +There is no leave period in between {0} and {1},Il n'y a pas de période de congé entre {0} et {1}, There is not enough leave balance for Leave Type {0},Il n'y a pas assez de solde de congés pour les Congés de Type {0}, There is nothing to edit.,Il n'y a rien à modifier., -There isn't any item variant for the selected item,Il n'y a pas de variante d'article pour l'article sélectionné, +There isn't any item variant for the selected item,Il n'y a pas de variante d'article pour l'article sélectionné, "There seems to be an issue with the server's GoCardless configuration. Don't worry, in case of failure, the amount will get refunded to your account.","Il semble y avoir un problème avec la configuration de GoCardless sur le serveur. Ne vous inquiétez pas, en cas d'échec, le montant sera remboursé sur votre compte.", There were errors creating Course Schedule,Des erreurs se sont produites lors de la création du programme, There were errors.,Il y a eu des erreurs., @@ -3108,7 +3108,7 @@ This is based on transactions against this Healthcare Practitioner.,Ce graphique This is based on transactions against this Patient. See timeline below for details,Ceci est basé sur les transactions de ce patient. Voir la chronologie ci-dessous pour plus de détails, This is based on transactions against this Sales Person. See timeline below for details,Ceci est basé sur les transactions contre ce vendeur. Voir la chronologie ci-dessous pour plus de détails, This is based on transactions against this Supplier. See timeline below for details,Basé sur les transactions avec ce fournisseur. Voir la chronologie ci-dessous pour plus de détails, -This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?,Cela permettra de soumettre des bulletins de salaire et de créer une écriture de journal d'accumulation. Voulez-vous poursuivre?, +This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?,Cela permettra de soumettre des bulletins de salaire et de créer une écriture de journal d'accumulation. Voulez-vous poursuivre?, This {0} conflicts with {1} for {2} {3},Ce {0} est en conflit avec {1} pour {2} {3}, Time Sheet for manufacturing.,Feuille de Temps pour la production., Time Tracking,Suivi du temps, @@ -3135,7 +3135,7 @@ To Date should be within the Fiscal Year. Assuming To Date = {0},La Date Finale To Datetime,À la Date, To Deliver,À Livrer, To Deliver and Bill,À Livrer et Facturer, -To Fiscal Year,À l'année fiscale, +To Fiscal Year,À l'année fiscale, To GSTIN,GSTIN (Destination), To Party Name,Nom du tiers (Destination), To Pin Code,Code postal (Destination), @@ -3151,7 +3151,7 @@ To date can not greater than employee's relieving date,La date de fin ne peut pa "To filter based on Party, select Party Type first","Pour filtrer en fonction du Tiers, sélectionnez d’abord le Type de Tiers", "To get the best out of ERPNext, we recommend that you take some time and watch these help videos.","Pour tirer le meilleur parti d’ERPNext, nous vous recommandons de prendre un peu de temps et de regarder ces vidéos d'aide.", "To include tax in row {0} in Item rate, taxes in rows {1} must also be included","Pour inclure la taxe de la ligne {0} dans le prix de l'Article, les taxes des lignes {1} doivent également être incluses", -To make Customer based incentive schemes.,Faire des programmes d'incitation basés sur le client., +To make Customer based incentive schemes.,Faire des programmes d'incitation basés sur le client., "To merge, following properties must be same for both items","Pour fusionner, les propriétés suivantes doivent être les mêmes pour les deux articles", "To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.","Pour ne pas appliquer la Règle de Tarification dans une transaction particulière, toutes les Règles de Tarification applicables doivent être désactivées.", "To set this Fiscal Year as Default, click on 'Set as Default'","Pour définir cet Exercice Fiscal par défaut, cliquez sur ""Définir par défaut""", @@ -3202,9 +3202,9 @@ Total Unpaid: {0},Total des Impayés : {0}, Total Variance,Variance totale, Total Weightage of all Assessment Criteria must be 100%,Le total des pondérations de tous les Critères d'Évaluation doit être égal à 100%, Total advance ({0}) against Order {1} cannot be greater than the Grand Total ({2}),Avance totale ({0}) pour la Commande {1} ne peut pas être supérieure au Total Général ({2}), -Total advance amount cannot be greater than total claimed amount,Le montant total de l'avance ne peut être supérieur au montant total réclamé, +Total advance amount cannot be greater than total claimed amount,Le montant total de l'avance ne peut être supérieur au montant total réclamé, Total advance amount cannot be greater than total sanctioned amount,Le montant total de l'avance ne peut être supérieur au montant total approuvé, -Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period,Le nombre total de congés alloués est supérieur de plusieurs jours à l'allocation maximale du type de congé {0} pour l'employé {1} au cours de la période, +Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period,Le nombre total de congés alloués est supérieur de plusieurs jours à l'allocation maximale du type de congé {0} pour l'employé {1} au cours de la période, Total allocated leaves are more than days in the period,Le Total des feuilles attribuées est supérieur au nombre de jours dans la période, Total allocated percentage for sales team should be 100,Pourcentage total attribué à l'équipe commerciale devrait être de 100, Total cannot be zero,Total ne peut pas être zéro, @@ -3303,18 +3303,18 @@ User Forum,Forum de l'Utilisateur, User ID,ID de l'Utilisateur, User ID not set for Employee {0},ID de l'Utilisateur non défini pour l'Employé {0}, User Remark,Remarque de l'Utilisateur, -User has not applied rule on the invoice {0},L'utilisateur n'a pas appliqué la règle sur la facture {0}, -User {0} already exists,L'utilisateur {0} existe déjà, +User has not applied rule on the invoice {0},L'utilisateur n'a pas appliqué la règle sur la facture {0}, +User {0} already exists,L'utilisateur {0} existe déjà, User {0} created,Utilisateur {0} créé, User {0} does not exist,Utilisateur {0} n'existe pas, -User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User.,L'utilisateur {0} n'a aucun profil POS par défaut. Vérifiez par défaut à la ligne {1} pour cet utilisateur., +User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User.,L'utilisateur {0} n'a aucun profil POS par défaut. Vérifiez par défaut à la ligne {1} pour cet utilisateur., User {0} is already assigned to Employee {1},Utilisateur {0} est déjà attribué à l'Employé {1}, -User {0} is already assigned to Healthcare Practitioner {1},L'utilisateur {0} est déjà attribué à un professionnel de la santé {1}, +User {0} is already assigned to Healthcare Practitioner {1},L'utilisateur {0} est déjà attribué à un professionnel de la santé {1}, Users,Utilisateurs, Utility Expenses,Frais de Services d'Utilité Publique, Valid From Date must be lesser than Valid Upto Date.,La date de début de validité doit être inférieure à la date de mise en service valide., Valid Till,Valable Jusqu'au, -Valid from and valid upto fields are mandatory for the cumulative,Les champs valides à partir de et valables jusqu'à sont obligatoires pour le cumulatif., +Valid from and valid upto fields are mandatory for the cumulative,Les champs valides à partir de et valables jusqu'à sont obligatoires pour le cumulatif., Valid from date must be less than valid upto date,La date de début de validité doit être inférieure à la date de validité, Valid till date cannot be before transaction date,La date de validité ne peut pas être avant la date de transaction, Validity,Validité, @@ -3327,7 +3327,7 @@ Value Proposition,Proposition de valeur, Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3} for Item {4},Valeur pour l'attribut {0} doit être dans la gamme de {1} à {2} dans les incréments de {3} pour le poste {4}, Value missing,Valeur manquante, Value must be between {0} and {1},La valeur doit être comprise entre {0} et {1}., -"Values of exempt, nil rated and non-GST inward supplies","Valeurs des fournitures importées exonérées, assorties d'une cote zéro et non liées à la TPS", +"Values of exempt, nil rated and non-GST inward supplies","Valeurs des fournitures importées exonérées, assorties d'une cote zéro et non liées à la TPS", Variable,Variable, Variance,Variance, Variance ({}),Variance ({}), @@ -3335,7 +3335,7 @@ Variant,Variante, Variant Attributes,Attributs Variant, Variant Based On cannot be changed,Les variantes basées sur ne peuvent pas être modifiées, Variant Details Report,Rapport détaillé des variantes, -Variant creation has been queued.,La création de variantes a été placée en file d'attente., +Variant creation has been queued.,La création de variantes a été placée en file d'attente., Vehicle Expenses,Frais de véhicule, Vehicle No,N° du Véhicule, Vehicle Type,Type de véhicule, @@ -3354,7 +3354,7 @@ Visit report for maintenance call.,Rapport de visite pour appel de maintenance, Visit the forums,Visitez les forums, Vital Signs,Signes vitaux, Volunteer,Bénévole, -Volunteer Type information.,Volontaire Type d'information., +Volunteer Type information.,Volontaire Type d'information., Volunteer information.,Informations sur le bénévolat, Voucher #,Référence #, Voucher No,N° de Référence, @@ -3367,7 +3367,7 @@ Warehouse is mandatory,L'entrepôt est obligatoire, Warehouse is mandatory for stock Item {0} in row {1},L’entrepôt est obligatoire pour l'Article du stock {0} dans la ligne {1}, Warehouse not found in the system,L'entrepôt n'a pas été trouvé dans le système, "Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}",Entrepôt requis à la ligne n ° {0}. Veuillez définir un entrepôt par défaut pour l'article {1} et la société {2}, -Warehouse required for stock Item {0},Magasin requis pour l'article en stock {0}, +Warehouse required for stock Item {0},Magasin requis pour l'article en stock {0}, Warehouse {0} can not be deleted as quantity exists for Item {1},L'entrepôt {0} ne peut pas être supprimé car il existe une quantité pour l'Article {1}, Warehouse {0} does not belong to company {1},L'entrepôt {0} n'appartient pas à la société {1}, Warehouse {0} does not exist,L'entrepôt {0} n'existe pas, @@ -3442,13 +3442,13 @@ You cannot credit and debit same account at the same time,Vous ne pouvez pas cr You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings,Vous ne pouvez pas supprimer l'exercice fiscal {0}. L'exercice fiscal {0} est défini par défaut dans les Paramètres Globaux, You cannot delete Project Type 'External',Vous ne pouvez pas supprimer le Type de Projet 'Externe', You cannot edit root node.,Vous ne pouvez pas modifier le nœud racine., -You cannot restart a Subscription that is not cancelled.,Vous ne pouvez pas redémarrer un abonnement qui n'est pas annulé., +You cannot restart a Subscription that is not cancelled.,Vous ne pouvez pas redémarrer un abonnement qui n'est pas annulé., You don't have enought Loyalty Points to redeem,Vous n'avez pas assez de points de fidélité à échanger, -You have already assessed for the assessment criteria {}.,Vous avez déjà évalué les critères d'évaluation {}., +You have already assessed for the assessment criteria {}.,Vous avez déjà évalué les critères d'évaluation {}., You have already selected items from {0} {1},Vous avez déjà choisi des articles de {0} {1}, You have been invited to collaborate on the project: {0},Vous avez été invité à collaborer sur le projet : {0}, You have entered duplicate items. Please rectify and try again.,Vous avez entré un doublon. Veuillez rectifier et essayer à nouveau., -You need to be a user other than Administrator with System Manager and Item Manager roles to register on Marketplace.,Vous devez être un utilisateur autre que l'administrateur avec les rôles System Manager et Item Manager pour vous inscrire sur Marketplace., +You need to be a user other than Administrator with System Manager and Item Manager roles to register on Marketplace.,Vous devez être un utilisateur autre que l'administrateur avec les rôles System Manager et Item Manager pour vous inscrire sur Marketplace., You need to be a user with System Manager and Item Manager roles to add users to Marketplace.,Vous devez être un utilisateur doté de rôles System Manager et Item Manager pour ajouter des utilisateurs à Marketplace., You need to be a user with System Manager and Item Manager roles to register on Marketplace.,Vous devez être un utilisateur avec des rôles System Manager et Item Manager pour vous inscrire sur Marketplace., You need to be logged in to access this page,Vous devez être connecté pour pouvoir accéder à cette page, @@ -3512,11 +3512,11 @@ on,sur, {0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}.,{0} est obligatoire. Peut-être qu’un enregistrement de Taux de Change n'est pas créé pour {1} et {2}., {0} is not a stock Item,{0} n'est pas un Article de stock, {0} is not a valid Batch Number for Item {1},{0} n'est pas un Numéro de Lot valide pour l’Article {1}, -{0} is not added in the table,{0} n'est pas ajouté dans la table, -{0} is not in Optional Holiday List,{0} n'est pas dans la liste des jours fériés facultatifs, -{0} is not in a valid Payroll Period,{0} n'est pas dans une période de paie valide, +{0} is not added in the table,{0} n'est pas ajouté dans la table, +{0} is not in Optional Holiday List,{0} n'est pas dans la liste des jours fériés facultatifs, +{0} is not in a valid Payroll Period,{0} n'est pas dans une période de paie valide, {0} is now the default Fiscal Year. Please refresh your browser for the change to take effect.,{0} est désormais l’Exercice par défaut. Veuillez actualiser la page pour que les modifications soient prises en compte., -{0} is on hold till {1},{0} est en attente jusqu'à {1}, +{0} is on hold till {1},{0} est en attente jusqu'à {1}, {0} item found.,{0} élément trouvé., {0} items found.,{0} éléments trouvés., {0} items in progress,{0} articles en cours, @@ -3525,8 +3525,8 @@ on,sur, {0} must be negative in return document,{0} doit être négatif dans le document de retour, {0} must be submitted,{0} doit être soumis, {0} not allowed to transact with {1}. Please change the Company.,{0} n'est pas autorisé à traiter avec {1}. Veuillez changer la société., -{0} not found for item {1},{0} introuvable pour l'élément {1}, -{0} parameter is invalid,Le paramètre {0} n'est pas valide, +{0} not found for item {1},{0} introuvable pour l'élément {1}, +{0} parameter is invalid,Le paramètre {0} n'est pas valide, {0} payment entries can not be filtered by {1},{0} écritures de paiement ne peuvent pas être filtrées par {1}, {0} should be a value between 0 and 100,{0} devrait être une valeur comprise entre 0 et 100, {0} units of [{1}](#Form/Item/{1}) found in [{2}](#Form/Warehouse/{2}),{0} unités de [{1}] (#Formulaire/Article/{1}) trouvées dans [{2}] (#Formulaire/Entrepôt/{2}), @@ -3536,7 +3536,7 @@ on,sur, {0} variants created.,{0} variantes créées., {0} {1} created,{0} {1} créé, {0} {1} does not exist,{0} {1} n'existe pas, -{0} {1} does not exist.,{0} {1} n'existe pas, +{0} {1} does not exist.,{0} {1} n'existe pas, {0} {1} has been modified. Please refresh.,{0} {1} a été modifié. Veuillez actualiser., {0} {1} has not been submitted so the action cannot be completed,"{0} {1} n'a pas été soumis, donc l'action ne peut pas être complétée", "{0} {1} is associated with {2}, but Party Account is {3}","{0} {1} est associé à {2}, mais le compte tiers est {3}", @@ -3548,8 +3548,8 @@ on,sur, {0} {1} is frozen,{0} {1} est gelée, {0} {1} is fully billed,{0} {1} est entièrement facturé, {0} {1} is not active,{0} {1} n'est pas actif, -{0} {1} is not associated with {2} {3},{0} {1} n'est pas associé à {2} {3}, -{0} {1} is not present in the parent company,{0} {1} n'est pas présent dans la société mère, +{0} {1} is not associated with {2} {3},{0} {1} n'est pas associé à {2} {3}, +{0} {1} is not present in the parent company,{0} {1} n'est pas présent dans la société mère, {0} {1} is not submitted,{0} {1} n'a pas été soumis, {0} {1} is {2},{0} {1} est {2}, {0} {1} must be submitted,{0} {1} doit être soumis, @@ -3608,28 +3608,28 @@ or,ou, Ageing Range 4,Gamme de vieillissement 4, Allocated amount cannot be greater than unadjusted amount,Le montant alloué ne peut être supérieur au montant non ajusté, Allocated amount cannot be negative,Le montant alloué ne peut être négatif, -"Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry","Le compte d'écart doit être un compte de type actif / passif, car cette entrée de stock est une entrée d'ouverture.", +"Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry","Le compte d'écart doit être un compte de type actif / passif, car cette entrée de stock est une entrée d'ouverture.", Error in some rows,Erreur dans certaines lignes, Import Successful,Importation réussie, -Please save first,S'il vous plaît enregistrer en premier, -Price not found for item {0} in price list {1},Prix non trouvé pour l'article {0} dans la liste de prix {1}, -Warehouse Type,Type d'entrepôt, -'Date' is required,'Date' est requis, +Please save first,S'il vous plaît enregistrer en premier, +Price not found for item {0} in price list {1},Prix non trouvé pour l'article {0} dans la liste de prix {1}, +Warehouse Type,Type d'entrepôt, +'Date' is required,'Date' est requis, Benefit,Avantage, Budgets,Budgets, Bundle Qty,Quantité de paquet, Company GSTIN,GSTIN de la Société, -Company field is required,Le champ de l'entreprise est obligatoire, +Company field is required,Le champ de l'entreprise est obligatoire, Creating Dimensions...,Créer des dimensions ..., Duplicate entry against the item code {0} and manufacturer {1},Dupliquer la saisie par rapport au code article {0} et au fabricant {1}, Import Chart Of Accounts from CSV / Excel files,Importer un graphique des comptes à partir de fichiers CSV / Excel, -Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers,GSTIN invalide! L'entrée que vous avez entrée ne correspond pas au format GSTIN pour les titulaires d'un UIN ou les fournisseurs de services OIDAR non résidents, +Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers,GSTIN invalide! L'entrée que vous avez entrée ne correspond pas au format GSTIN pour les titulaires d'un UIN ou les fournisseurs de services OIDAR non résidents, Invoice Grand Total,Total général de la facture, Last carbon check date cannot be a future date,La date du dernier bilan carbone ne peut pas être une date future, Make Stock Entry,Faire une entrée de stock, Quality Feedback,Commentaires sur la qualité, Quality Feedback Template,Modèle de commentaires sur la qualité, -Rules for applying different promotional schemes.,Règles d'application de différents programmes promotionnels., +Rules for applying different promotional schemes.,Règles d'application de différents programmes promotionnels., Shift,Décalage, Show {0},Montrer {0}, "Special Characters except ""-"", ""#"", ""."", ""/"", ""{"" and ""}"" not allowed in naming series","Caractères spéciaux sauf "-", "#", ".", "/", "{" Et "}" non autorisés dans les séries de nommage", @@ -3649,7 +3649,7 @@ No data to export,Aucune donnée à exporter, Print Heading,Imprimer Titre, Video,Vidéo, % Of Grand Total,% Du grand total, -'employee_field_value' and 'timestamp' are required.,'employee_field_value' et 'timestamp' sont obligatoires., +'employee_field_value' and 'timestamp' are required.,'employee_field_value' et 'timestamp' sont obligatoires., Company is a mandatory filter.,La société est un filtre obligatoire., From Date is a mandatory filter.,De la date est un filtre obligatoire., From Time cannot be later than To Time for {0},From Time ne peut pas être postérieur à To Time pour {0}, @@ -3657,13 +3657,13 @@ Video,Vidéo, A new appointment has been created for you with {0},Un nouveau rendez-vous a été créé pour vous avec {0}, Account Value,Valeur du compte, Account is mandatory to get payment entries,Le compte est obligatoire pour obtenir les entrées de paiement, -Account is not set for the dashboard chart {0},Le compte n'est pas défini pour le graphique du tableau de bord {0}, +Account is not set for the dashboard chart {0},Le compte n'est pas défini pour le graphique du tableau de bord {0}, Account {0} does not belong to company {1},Compte {0} n'appartient pas à la société {1}, -Account {0} does not exists in the dashboard chart {1},Le compte {0} n'existe pas dans le graphique du tableau de bord {1}, +Account {0} does not exists in the dashboard chart {1},Le compte {0} n'existe pas dans le graphique du tableau de bord {1}, Account: {0} is capital Work in progress and can not be updated by Journal Entry,Compte: {0} est un travail capital et ne peut pas être mis à jour par une écriture au journal., -Account: {0} is not permitted under Payment Entry,Compte: {0} n'est pas autorisé sous Saisie du paiement., +Account: {0} is not permitted under Payment Entry,Compte: {0} n'est pas autorisé sous Saisie du paiement., Accounting Dimension {0} is required for 'Balance Sheet' account {1}.,La dimension de comptabilité {0} est requise pour le compte "Bilan" {1}., -Accounting Dimension {0} is required for 'Profit and Loss' account {1}.,La dimension de comptabilité {0} est requise pour le compte 'Bénéfices et pertes' {1}., +Accounting Dimension {0} is required for 'Profit and Loss' account {1}.,La dimension de comptabilité {0} est requise pour le compte 'Bénéfices et pertes' {1}., Accounting Masters,Maîtres Comptables, Accounting Period overlaps with {0},La période comptable chevauche avec {0}, Activity,Activité, @@ -3672,14 +3672,14 @@ Add Child,Ajouter une Sous-Catégorie, Add Loan Security,Ajouter une garantie de prêt, Add Multiple,Ajout Multiple, Add Participants,Ajouter des participants, -Add to Featured Item,Ajouter à l'article en vedette, +Add to Featured Item,Ajouter à l'article en vedette, Add your review,Ajouter votre avis, Add/Edit Coupon Conditions,Ajouter / Modifier les conditions du coupon, Added to Featured Items,Ajouté aux articles en vedette, Added {0} ({1}),Ajouté {0} ({1}), Address Line 1,Adresse Ligne 1, Addresses,Adresses, -Admission End Date should be greater than Admission Start Date.,La date de fin d'admission doit être supérieure à la date de début d'admission., +Admission End Date should be greater than Admission Start Date.,La date de fin d'admission doit être supérieure à la date de début d'admission., Against Loan,Contre le prêt, Against Loan:,Contre le prêt:, All,Tout, @@ -3693,35 +3693,35 @@ Applied Coupon Code,Code de coupon appliqué, Apply Coupon Code,Appliquer le code de coupon, Appointment Booking,Prise de rendez-vous, "As there are existing transactions against item {0}, you can not change the value of {1}","Comme il existe des transactions avec l'article {0}, vous ne pouvez pas changer la valeur de {1}", -Asset Id,ID d'actif, -Asset Value,Valeur d'actif, -Asset Value Adjustment cannot be posted before Asset's purchase date {0}.,L'ajustement de la valeur de l'actif ne peut pas être enregistré avant la date d'achat de l'actif {0} ., -Asset {0} does not belongs to the custodian {1},L'élément {0} n'appartient pas au dépositaire {1}, -Asset {0} does not belongs to the location {1},L'élément {0} n'appartient pas à l'emplacement {1}, +Asset Id,ID d'actif, +Asset Value,Valeur d'actif, +Asset Value Adjustment cannot be posted before Asset's purchase date {0}.,L'ajustement de la valeur de l'actif ne peut pas être enregistré avant la date d'achat de l'actif {0} ., +Asset {0} does not belongs to the custodian {1},L'élément {0} n'appartient pas au dépositaire {1}, +Asset {0} does not belongs to the location {1},L'élément {0} n'appartient pas à l'emplacement {1}, At least one of the Applicable Modules should be selected,Au moins un des modules applicables doit être sélectionné, Atleast one asset has to be selected.,Au moins un actif doit être sélectionné., Attendance Marked,Présence marquée, Attendance has been marked as per employee check-ins,La présence a été marquée selon les enregistrements des employés, Authentication Failed,Authentification échouée, Automatic Reconciliation,Rapprochement automatique, -Available For Use Date,Date d'utilisation disponible, +Available For Use Date,Date d'utilisation disponible, Available Stock,Stock disponible, "Available quantity is {0}, you need {1}",La quantité disponible est {0}. Vous avez besoin de {1}., BOM 1,BOM 1, BOM 2,BOM 2, BOM Comparison Tool,Outil de comparaison de nomenclature, BOM recursion: {0} cannot be child of {1},Récursion de nomenclature: {0} ne peut pas être enfant de {1}, -BOM recursion: {0} cannot be parent or child of {1},Récursion de nomenclature: {0} ne peut pas être le parent ou l'enfant de {1}, +BOM recursion: {0} cannot be parent or child of {1},Récursion de nomenclature: {0} ne peut pas être le parent ou l'enfant de {1}, Back to Home,De retour à la maison, Back to Messages,Retour aux messages, -Bank Data mapper doesn't exist,Bank Data Mapper n'existe pas, +Bank Data mapper doesn't exist,Bank Data Mapper n'existe pas, Bank Details,Coordonnées bancaires, -Bank account '{0}' has been synchronized,Le compte bancaire '{0}' a été synchronisé, -Bank account {0} already exists and could not be created again,Le compte bancaire {0} existe déjà et n'a pas pu être créé à nouveau., +Bank account '{0}' has been synchronized,Le compte bancaire '{0}' a été synchronisé, +Bank account {0} already exists and could not be created again,Le compte bancaire {0} existe déjà et n'a pas pu être créé à nouveau., Bank accounts added,Comptes bancaires ajoutés, -Batch no is required for batched item {0},Le numéro de lot est requis pour l'article en lot {0}., +Batch no is required for batched item {0},Le numéro de lot est requis pour l'article en lot {0}., Billing Date,Date de facturation, -Billing Interval Count cannot be less than 1,Le nombre d'intervalles de facturation ne peut pas être inférieur à 1, +Billing Interval Count cannot be less than 1,Le nombre d'intervalles de facturation ne peut pas être inférieur à 1, Blue,Bleu, Book,Livre, Book Appointment,Prendre rendez-vous, @@ -3730,18 +3730,18 @@ Browse,Feuilleter, Call Connected,Appel connecté, Call Disconnected,Appel déconnecté, Call Missed,Appel manqué, -Call Summary,Résumé d'appel, -Call Summary Saved,Résumé de l'appel enregistré, +Call Summary,Résumé d'appel, +Call Summary Saved,Résumé de l'appel enregistré, Cancelled,Annulé, -Cannot Calculate Arrival Time as Driver Address is Missing.,Impossible de calculer l'heure d'arrivée car l'adresse du conducteur est manquante., -Cannot Optimize Route as Driver Address is Missing.,Impossible d'optimiser l'itinéraire car l'adresse du pilote est manquante., +Cannot Calculate Arrival Time as Driver Address is Missing.,Impossible de calculer l'heure d'arrivée car l'adresse du conducteur est manquante., +Cannot Optimize Route as Driver Address is Missing.,Impossible d'optimiser l'itinéraire car l'adresse du pilote est manquante., "Cannot Unpledge, loan security value is greater than the repaid amount","Impossible de désengager, la valeur de la garantie de prêt est supérieure au montant remboursé", -Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,Impossible de terminer la tâche {0} car sa tâche dépendante {1} n'est pas terminée / annulée., -Cannot create loan until application is approved,Impossible de créer un prêt tant que la demande n'est pas approuvée, +Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,Impossible de terminer la tâche {0} car sa tâche dépendante {1} n'est pas terminée / annulée., +Cannot create loan until application is approved,Impossible de créer un prêt tant que la demande n'est pas approuvée, Cannot find a matching Item. Please select some other value for {0}.,Impossible de trouver un article similaire. Veuillez sélectionner une autre valeur pour {0}., "Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","La surfacturation pour le poste {0} dans la ligne {1} ne peut pas dépasser {2}. Pour autoriser la surfacturation, définissez la provision dans les paramètres du compte.", Cannot unpledge more than {0} qty of {0},Impossible de retirer plus de {0} quantité de {0}, -"Capacity Planning Error, planned start time can not be same as end time","Erreur de planification de capacité, l'heure de début prévue ne peut pas être identique à l'heure de fin", +"Capacity Planning Error, planned start time can not be same as end time","Erreur de planification de capacité, l'heure de début prévue ne peut pas être identique à l'heure de fin", Categories,Catégories, Changes in {0},Changements dans {0}, Chart,Graphique, @@ -3751,26 +3751,26 @@ Close,Fermer, Communication,la communication, Compact Item Print,Impression de l'Article Compacté, Company,Société, -Company of asset {0} and purchase document {1} doesn't matches.,La société de l'actif {0} et le document d'achat {1} ne correspondent pas., +Company of asset {0} and purchase document {1} doesn't matches.,La société de l'actif {0} et le document d'achat {1} ne correspondent pas., Compare BOMs for changes in Raw Materials and Operations,Comparer les nomenclatures aux modifications apportées aux matières premières et aux opérations, Compare List function takes on list arguments,La fonction de comparaison de liste accepte les arguments de liste, Complete,Terminé, Completed,Terminé, Completed Quantity,Quantité terminée, -Connect your Exotel Account to ERPNext and track call logs,Connectez votre compte Exotel à ERPNext et suivez les journaux d'appels, +Connect your Exotel Account to ERPNext and track call logs,Connectez votre compte Exotel à ERPNext et suivez les journaux d'appels, Connect your bank accounts to ERPNext,Connectez vos comptes bancaires à ERPNext, Contact Seller,Contacter le vendeur, Continue,Continuer, -Cost Center: {0} does not exist,Centre de coûts: {0} n'existe pas, +Cost Center: {0} does not exist,Centre de coûts: {0} n'existe pas, Couldn't Set Service Level Agreement {0}.,Impossible de définir le contrat de service {0}., Country,Pays, Country Code in File does not match with country code set up in the system,Le code de pays dans le fichier ne correspond pas au code de pays configuré dans le système, Create New Contact,Créer un nouveau contact, Create New Lead,Créer une nouvelle piste, Create Pick List,Créer une liste de choix, -Create Quality Inspection for Item {0},Créer un contrôle qualité pour l'article {0}, +Create Quality Inspection for Item {0},Créer un contrôle qualité pour l'article {0}, Creating Accounts...,Création de comptes ..., -Creating bank entries...,Création d'entrées bancaires ..., +Creating bank entries...,Création d'entrées bancaires ..., Creating {0},Création de {0}, Credit limit is already defined for the Company {0},La limite de crédit est déjà définie pour la société {0}., Ctrl + Enter to submit,Ctrl + Entrée pour soumettre, @@ -3782,7 +3782,7 @@ Customize,Personnaliser, Daily,Quotidien, Date,Date, Date Range,Intervalle de Date, -Date of Birth cannot be greater than Joining Date.,La date de naissance ne peut pas être supérieure à la date d'adhésion., +Date of Birth cannot be greater than Joining Date.,La date de naissance ne peut pas être supérieure à la date d'adhésion., Dear,Cher/Chère, Default,Par Défaut, Define coupon codes.,Définissez les codes promo., @@ -3808,40 +3808,40 @@ Due Date,Date d'Échéance, Duplicate,Dupliquer, Duplicate Project with Tasks,Projet en double avec tâches, Duplicate project has been created,Un projet en double a été créé, -E-Way Bill JSON can only be generated from a submitted document,E-Way Bill JSON ne peut être généré qu'à partir d'un document soumis, -E-Way Bill JSON can only be generated from submitted document,E-Way Bill JSON ne peut être généré qu'à partir du document soumis, +E-Way Bill JSON can only be generated from a submitted document,E-Way Bill JSON ne peut être généré qu'à partir d'un document soumis, +E-Way Bill JSON can only be generated from submitted document,E-Way Bill JSON ne peut être généré qu'à partir du document soumis, E-Way Bill JSON cannot be generated for Sales Return as of now,La facture e-Way JSON ne peut pas être générée pour le retour de vente à partir de maintenant, -ERPNext could not find any matching payment entry,ERPNext n'a trouvé aucune entrée de paiement correspondante, +ERPNext could not find any matching payment entry,ERPNext n'a trouvé aucune entrée de paiement correspondante, Earliest Age,Âge le plus précoce, Edit Details,Modifier les détails, Edit Profile,Modifier le Profil, -Either GST Transporter ID or Vehicle No is required if Mode of Transport is Road,Un numéro d'identification de transporteur ou un numéro de véhicule est requis si le mode de transport est la route., +Either GST Transporter ID or Vehicle No is required if Mode of Transport is Road,Un numéro d'identification de transporteur ou un numéro de véhicule est requis si le mode de transport est la route., Email,Email, Email Campaigns,Campagnes de courrier électronique, -Employee ID is linked with another instructor,L'ID de l'employé est lié à un autre instructeur, +Employee ID is linked with another instructor,L'ID de l'employé est lié à un autre instructeur, Employee Tax and Benefits,Impôt et avantages sociaux des employés, -Employee is required while issuing Asset {0},L'employé est requis lors de l'émission de l'actif {0}, -Employee {0} does not belongs to the company {1},L'employé {0} n'appartient pas à l'entreprise {1}, +Employee is required while issuing Asset {0},L'employé est requis lors de l'émission de l'actif {0}, +Employee {0} does not belongs to the company {1},L'employé {0} n'appartient pas à l'entreprise {1}, Enable Auto Re-Order,Activer la re-commande automatique, -End Date of Agreement can't be less than today.,La date de fin de l'accord ne peut être inférieure à celle d'aujourd'hui., +End Date of Agreement can't be less than today.,La date de fin de l'accord ne peut être inférieure à celle d'aujourd'hui., End Time,Heure de fin, Energy Point Leaderboard,Point de classement énergétique, Enter API key in Google Settings.,Entrez la clé API dans les paramètres Google., Enter Supplier,Entrez le fournisseur, Enter Value,Entrez une valeur, -Entity Type,Type d'entité, +Entity Type,Type d'entité, Error,Erreur, Error in Exotel incoming call,Erreur dans un appel entrant Exotel, Error: {0} is mandatory field,Erreur: {0} est un champ obligatoire, -Event Link,Lien d'événement, -Exception occurred while reconciling {0},Une exception s'est produite lors de la réconciliation {0}, -Expected and Discharge dates cannot be less than Admission Schedule date,Les dates prévues et de sortie ne peuvent pas être inférieures à la date du calendrier d'admission, -Expire Allocation,Expiration de l'allocation, +Event Link,Lien d'événement, +Exception occurred while reconciling {0},Une exception s'est produite lors de la réconciliation {0}, +Expected and Discharge dates cannot be less than Admission Schedule date,Les dates prévues et de sortie ne peuvent pas être inférieures à la date du calendrier d'admission, +Expire Allocation,Expiration de l'allocation, Expired,Expiré, Export,Exporter, Export not allowed. You need {0} role to export.,Pas autorisé à exporter. Vous devez avoir le rôle {0} pour exporter., -Failed to add Domain,Impossible d'ajouter le domaine, -Fetch Items from Warehouse,Récupérer des articles de l'entrepôt, +Failed to add Domain,Impossible d'ajouter le domaine, +Fetch Items from Warehouse,Récupérer des articles de l'entrepôt, Fetching...,Aller chercher..., Field,Champ, File Manager,Gestionnaire de fichiers, @@ -3852,13 +3852,13 @@ Finished Qty,Quantité finie, Fleet Management,Gestion de flotte, Following fields are mandatory to create address:,Les champs suivants sont obligatoires pour créer une adresse:, For Month,Pour mois, -"For item {0} at row {1}, count of serial numbers does not match with the picked quantity","Pour l'élément {0} à la ligne {1}, le nombre de numéros de série ne correspond pas à la quantité sélectionnée.", -For operation {0}: Quantity ({1}) can not be greter than pending quantity({2}),Pour l'opération {0}: la quantité ({1}) ne peut pas être supérieure à la quantité en attente ({2}), -For quantity {0} should not be greater than work order quantity {1},Pour la quantité {0} ne doit pas être supérieure à la quantité d'ordre de travail {1}, +"For item {0} at row {1}, count of serial numbers does not match with the picked quantity","Pour l'élément {0} à la ligne {1}, le nombre de numéros de série ne correspond pas à la quantité sélectionnée.", +For operation {0}: Quantity ({1}) can not be greter than pending quantity({2}),Pour l'opération {0}: la quantité ({1}) ne peut pas être supérieure à la quantité en attente ({2}), +For quantity {0} should not be greater than work order quantity {1},Pour la quantité {0} ne doit pas être supérieure à la quantité d'ordre de travail {1}, Free item not set in the pricing rule {0},Article gratuit non défini dans la règle de tarification {0}, From Date and To Date are Mandatory,La date de début et la date de fin sont obligatoires, From date can not be greater than than To date,La date de début ne peut être supérieure à la date de fin, -From employee is required while receiving Asset {0} to a target location,De l'employé est requis lors de la réception de l'actif {0} vers un emplacement cible, +From employee is required while receiving Asset {0} to a target location,De l'employé est requis lors de la réception de l'actif {0} vers un emplacement cible, Fuel Expense,Frais de carburant, Future Payment Amount,Montant du paiement futur, Future Payment Ref,Paiement futur Ref, @@ -3880,27 +3880,27 @@ Help Article,Article d’Aide, "Helps you keep tracks of Contracts based on Supplier, Customer and Employee","Vous aide à garder une trace des contrats en fonction du fournisseur, client et employé", Helps you manage appointments with your leads,Vous aide à gérer les rendez-vous avec vos prospects, Home,Accueil, -IBAN is not valid,IBAN n'est pas valide, +IBAN is not valid,IBAN n'est pas valide, Import Data from CSV / Excel files.,Importer des données à partir de fichiers CSV / Excel, In Progress,En cours, Incoming call from {0},Appel entrant du {0}, Incorrect Warehouse,Entrepôt incorrect, Interest Amount is mandatory,Le montant des intérêts est obligatoire, Intermediate,Intermédiaire, -Invalid Barcode. There is no Item attached to this barcode.,Code à barres invalide. Il n'y a pas d'article attaché à ce code à barres., -Invalid credentials,les informations d'identification invalides, +Invalid Barcode. There is no Item attached to this barcode.,Code à barres invalide. Il n'y a pas d'article attaché à ce code à barres., +Invalid credentials,les informations d'identification invalides, Invite as User,Inviter en tant qu'Utilisateur, -Issue Priority.,Priorité d'émission., +Issue Priority.,Priorité d'émission., Issue Type.,Type de probleme., "It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.","Il semble qu'il y a un problème avec la configuration de Stripe sur le serveur. En cas d'erreur, le montant est remboursé sur votre compte.", Item Reported,Article rapporté, -Item listing removed,Liste d'articles supprimée, -Item quantity can not be zero,La quantité d'article ne peut être nulle, +Item listing removed,Liste d'articles supprimée, +Item quantity can not be zero,La quantité d'article ne peut être nulle, Item taxes updated,Taxes sur les articles mises à jour, Item {0}: {1} qty produced. ,Article {0}: {1} quantité produite., Items are required to pull the raw materials which is associated with it.,Les articles sont nécessaires pour extraire les matières premières qui lui sont associées., -Joining Date can not be greater than Leaving Date,La date d'adhésion ne peut pas être supérieure à la date de départ, -Lab Test Item {0} already exist,L'élément de test en laboratoire {0} existe déjà, +Joining Date can not be greater than Leaving Date,La date d'adhésion ne peut pas être supérieure à la date de départ, +Lab Test Item {0} already exist,L'élément de test en laboratoire {0} existe déjà, Last Issue,Dernier numéro, Latest Age,Dernier âge, Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay,La demande de congé est liée aux allocations de congé {0}. Demande de congé ne peut pas être défini comme congé sans solde, @@ -3910,7 +3910,7 @@ Liabilities,Passifs, Loading...,Chargement en Cours ..., Loan Amount exceeds maximum loan amount of {0} as per proposed securities,Le montant du prêt dépasse le montant maximal du prêt de {0} selon les titres proposés, Loan Applications from customers and employees.,Demandes de prêt des clients et des employés., -Loan Disbursement,Déboursement de l'emprunt, +Loan Disbursement,Déboursement de l'emprunt, Loan Processes,Processus de prêt, Loan Security,Sécurité des prêts, Loan Security Pledge,Garantie de prêt, @@ -3922,7 +3922,7 @@ Loan Security Price,Prix de la sécurité du prêt, Loan Security Price overlapping with {0},Le prix du titre de crédit se chevauche avec {0}, Loan Security Unpledge,Désengagement de garantie de prêt, Loan Security Value,Valeur de la sécurité du prêt, -Loan Type for interest and penalty rates,Type de prêt pour les taux d'intérêt et de pénalité, +Loan Type for interest and penalty rates,Type de prêt pour les taux d'intérêt et de pénalité, Loan amount cannot be greater than {0},Le montant du prêt ne peut pas être supérieur à {0}, Loan is mandatory,Le prêt est obligatoire, Loans,Les prêts, @@ -3951,17 +3951,17 @@ New Payment,Nouveau paiement, New release date should be in the future,La nouvelle date de sortie devrait être dans le futur, Newsletter,Newsletter, No Account matched these filters: {},Aucun compte ne correspond à ces filtres: {}, -No Employee found for the given employee field value. '{}': {},Aucun employé trouvé pour la valeur de champ d'employé donnée. '{}': {}, -No Leaves Allocated to Employee: {0} for Leave Type: {1},Aucun congé attribué à l'employé: {0} pour le type de congé: {1}, +No Employee found for the given employee field value. '{}': {},Aucun employé trouvé pour la valeur de champ d'employé donnée. '{}': {}, +No Leaves Allocated to Employee: {0} for Leave Type: {1},Aucun congé attribué à l'employé: {0} pour le type de congé: {1}, No communication found.,Aucune communication trouvée., -No correct answer is set for {0},Aucune réponse correcte n'est définie pour {0}., +No correct answer is set for {0},Aucune réponse correcte n'est définie pour {0}., No description,Pas de description, -No issue has been raised by the caller.,Aucun problème n'a été soulevé par l'appelant., +No issue has been raised by the caller.,Aucun problème n'a été soulevé par l'appelant., No items to publish,Aucun élément à publier, No outstanding invoices found,Aucune facture en attente trouvée, -No outstanding invoices found for the {0} {1} which qualify the filters you have specified.,Aucune facture en attente n'a été trouvée pour le {0} {1} qui qualifie les filtres que vous avez spécifiés., +No outstanding invoices found for the {0} {1} which qualify the filters you have specified.,Aucune facture en attente n'a été trouvée pour le {0} {1} qui qualifie les filtres que vous avez spécifiés., No outstanding invoices require exchange rate revaluation,Aucune facture en attente ne nécessite une réévaluation du taux de change, -No reviews yet,Pas encore d'avis, +No reviews yet,Pas encore d'avis, No views yet,Pas encore de vue, Non stock items,Articles hors stock, Not Allowed,Non Autorisé, @@ -3970,25 +3970,25 @@ Not permitted. Please disable the Lab Test Template,Pas permis. Veuillez désact Note,Note, Notes: ,Remarques :, Offline,Hors ligne, -On Converting Opportunity,Sur l'opportunité de conversion, +On Converting Opportunity,Sur l'opportunité de conversion, On Purchase Order Submission,Sur soumission de commande, On Sales Order Submission,Envoi de commande client, On Task Completion,En fin de tâche, On {0} Creation,Sur {0} Creation, Only .csv and .xlsx files are supported currently,Seuls les fichiers .csv et .xlsx sont actuellement pris en charge., -Only expired allocation can be cancelled,Seule l'allocation expirée peut être annulée, +Only expired allocation can be cancelled,Seule l'allocation expirée peut être annulée, Only users with the {0} role can create backdated leave applications,Seuls les utilisateurs avec le rôle {0} peuvent créer des demandes de congé antidatées, Open,Ouvert, Open Contact,Contact ouvert, Open Lead,Ouvrir le fil, Opening and Closing,Ouverture et fermeture, -Operating Cost as per Work Order / BOM,Coût d'exploitation selon l'ordre de travail / nomenclature, +Operating Cost as per Work Order / BOM,Coût d'exploitation selon l'ordre de travail / nomenclature, Order Amount,Montant de la commande, Page {0} of {1},Page {0} sur {1}, Paid amount cannot be less than {0},Le montant payé ne peut pas être inférieur à {0}, Parent Company must be a group company,La société mère doit être une société du groupe, Passing Score value should be between 0 and 100,La note de passage doit être comprise entre 0 et 100, -Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically,La politique de mot de passe ne peut pas contenir d'espaces ni de traits d'union simultanés. Le format sera restructuré automatiquement, +Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically,La politique de mot de passe ne peut pas contenir d'espaces ni de traits d'union simultanés. Le format sera restructuré automatiquement, Patient History,Histoire du patient, Pause,Pause, Pay,Payer, @@ -4001,39 +4001,39 @@ Period based On,Période basée sur, Perpetual inventory required for the company {0} to view this report.,Inventaire permanent requis pour que la société {0} puisse consulter ce rapport., Phone,Téléphone, Pick List,Liste de sélection, -Plaid authentication error,Erreur d'authentification du plaid, +Plaid authentication error,Erreur d'authentification du plaid, Plaid public token error,Erreur de jeton public Plaid, Plaid transactions sync error,Erreur de synchronisation des transactions plaid, -Please check the error log for details about the import errors,Veuillez consulter le journal des erreurs pour plus de détails sur les erreurs d'importation., +Please check the error log for details about the import errors,Veuillez consulter le journal des erreurs pour plus de détails sur les erreurs d'importation., Please click on the following link to set your new password,Veuillez cliquer sur le lien suivant pour définir votre nouveau mot de passe, -Please create DATEV Settings for Company {}.,Veuillez créer les paramètres DATEV pour l'entreprise {} ., -Please create adjustment Journal Entry for amount {0} ,Veuillez créer une écriture de journal d'ajustement pour le montant {0}, +Please create DATEV Settings for Company {}.,Veuillez créer les paramètres DATEV pour l'entreprise {} ., +Please create adjustment Journal Entry for amount {0} ,Veuillez créer une écriture de journal d'ajustement pour le montant {0}, Please do not create more than 500 items at a time,Ne créez pas plus de 500 objets à la fois., -Please enter Difference Account or set default Stock Adjustment Account for company {0},Veuillez saisir un compte d'écart ou définir un compte d'ajustement de stock par défaut pour la société {0}, -Please enter GSTIN and state for the Company Address {0},Veuillez saisir GSTIN et indiquer l'adresse de la société {0}., -Please enter Item Code to get item taxes,Veuillez entrer le code de l'article pour obtenir les taxes sur les articles, +Please enter Difference Account or set default Stock Adjustment Account for company {0},Veuillez saisir un compte d'écart ou définir un compte d'ajustement de stock par défaut pour la société {0}, +Please enter GSTIN and state for the Company Address {0},Veuillez saisir GSTIN et indiquer l'adresse de la société {0}., +Please enter Item Code to get item taxes,Veuillez entrer le code de l'article pour obtenir les taxes sur les articles, Please enter Warehouse and Date,Veuillez entrer entrepôt et date, -Please enter coupon code !!,S'il vous plaît entrer le code coupon !!, -Please enter the designation,S'il vous plaît entrer la désignation, +Please enter coupon code !!,S'il vous plaît entrer le code coupon !!, +Please enter the designation,S'il vous plaît entrer la désignation, Please enter valid coupon code !!,Veuillez entrer un code de coupon valide !!, -Please login as a Marketplace User to edit this item.,Veuillez vous connecter en tant qu'utilisateur Marketplace pour modifier cet article., -Please login as a Marketplace User to report this item.,Veuillez vous connecter en tant qu'utilisateur de la Marketplace pour signaler cet élément., +Please login as a Marketplace User to edit this item.,Veuillez vous connecter en tant qu'utilisateur Marketplace pour modifier cet article., +Please login as a Marketplace User to report this item.,Veuillez vous connecter en tant qu'utilisateur de la Marketplace pour signaler cet élément., Please select Template Type to download template,Veuillez sélectionner le type de modèle pour télécharger le modèle, -Please select Applicant Type first,Veuillez d'abord sélectionner le type de demandeur, -Please select Customer first,S'il vous plaît sélectionnez d'abord le client, -Please select Item Code first,Veuillez d'abord sélectionner le code d'article, +Please select Applicant Type first,Veuillez d'abord sélectionner le type de demandeur, +Please select Customer first,S'il vous plaît sélectionnez d'abord le client, +Please select Item Code first,Veuillez d'abord sélectionner le code d'article, Please select Loan Type for company {0},Veuillez sélectionner le type de prêt pour la société {0}, Please select a Delivery Note,Veuillez sélectionner un bon de livraison, -Please select a Sales Person for item: {0},Veuillez sélectionner un commercial pour l'article: {0}, +Please select a Sales Person for item: {0},Veuillez sélectionner un commercial pour l'article: {0}, Please select another payment method. Stripe does not support transactions in currency '{0}',Veuillez sélectionner une autre méthode de paiement. Stripe ne prend pas en charge les transactions en devise '{0}', -Please select the customer.,S'il vous plaît sélectionner le client., +Please select the customer.,S'il vous plaît sélectionner le client., Please set a Supplier against the Items to be considered in the Purchase Order.,Veuillez définir un fournisseur par rapport aux articles à prendre en compte dans le bon de commande., Please set account heads in GST Settings for Compnay {0},Définissez les en-têtes de compte dans les paramètres de la TPS pour le service {0}., Please set an email id for the Lead {0},Veuillez définir un identifiant de messagerie pour le prospect {0}., -Please set default UOM in Stock Settings,Veuillez définir l'UdM par défaut dans les paramètres de stock, -Please set filter based on Item or Warehouse due to a large amount of entries.,Veuillez définir le filtre en fonction de l'article ou de l'entrepôt en raison d'une grande quantité d'entrées., +Please set default UOM in Stock Settings,Veuillez définir l'UdM par défaut dans les paramètres de stock, +Please set filter based on Item or Warehouse due to a large amount of entries.,Veuillez définir le filtre en fonction de l'article ou de l'entrepôt en raison d'une grande quantité d'entrées., Please set up the Campaign Schedule in the Campaign {0},Configurez le calendrier de la campagne dans la campagne {0}., -Please set valid GSTIN No. in Company Address for company {0},Veuillez définir un numéro GSTIN valide dans l'adresse de l'entreprise pour l'entreprise {0}, +Please set valid GSTIN No. in Company Address for company {0},Veuillez définir un numéro GSTIN valide dans l'adresse de l'entreprise pour l'entreprise {0}, Please set {0},Veuillez définir {0},customer Please setup a default bank account for company {0},Veuillez configurer un compte bancaire par défaut pour la société {0}., Please specify,Veuillez spécifier, @@ -4048,29 +4048,29 @@ Processing XML Files,Traitement des fichiers XML, Profitability,Rentabilité, Project,Projet, Proposed Pledges are mandatory for secured Loans,Les engagements proposés sont obligatoires pour les prêts garantis, -Provide the academic year and set the starting and ending date.,Indiquez l'année universitaire et définissez la date de début et de fin., +Provide the academic year and set the starting and ending date.,Indiquez l'année universitaire et définissez la date de début et de fin., Public token is missing for this bank,Un jeton public est manquant pour cette banque, Publish,Publier, Publish 1 Item,Publier 1 élément, Publish Items,Publier des articles, -Publish More Items,Publier plus d'articles, +Publish More Items,Publier plus d'articles, Publish Your First Items,Publiez vos premiers articles, Publish {0} Items,Publier {0} éléments, Published Items,Articles publiés, -Purchase Invoice cannot be made against an existing asset {0},La facture d'achat ne peut pas être effectuée sur un élément existant {0}, -Purchase Invoices,Factures d'achat, +Purchase Invoice cannot be made against an existing asset {0},La facture d'achat ne peut pas être effectuée sur un élément existant {0}, +Purchase Invoices,Factures d'achat, Purchase Orders,Acheter en ligne, Purchase Receipt doesn't have any Item for which Retain Sample is enabled.,Le reçu d’achat ne contient aucun élément pour lequel Conserver échantillon est activé., Purchase Return,Retour d'Achat, Qty of Finished Goods Item,Quantité de produits finis, Qty or Amount is mandatroy for loan security,La quantité ou le montant est obligatoire pour la garantie de prêt, -Quality Inspection required for Item {0} to submit,Inspection de qualité requise pour que l'élément {0} soit envoyé, +Quality Inspection required for Item {0} to submit,Inspection de qualité requise pour que l'élément {0} soit envoyé, Quantity to Manufacture,Quantité à fabriquer, -Quantity to Manufacture can not be zero for the operation {0},La quantité à fabriquer ne peut pas être nulle pour l'opération {0}, +Quantity to Manufacture can not be zero for the operation {0},La quantité à fabriquer ne peut pas être nulle pour l'opération {0}, Quarterly,Trimestriel, Queued,File d'Attente, Quick Entry,Écriture Rapide, -Quiz {0} does not exist,Le questionnaire {0} n'existe pas, +Quiz {0} does not exist,Le questionnaire {0} n'existe pas, Quotation Amount,Montant du devis, Rate or Discount is required for the price discount.,Le taux ou la remise est requis pour la remise de prix., Reason,Raison, @@ -4081,7 +4081,7 @@ Recruitment,Recrutement, Red,rouge, Refreshing,Rafraîchissant, Release date must be in the future,La date de sortie doit être dans le futur, -Relieving Date must be greater than or equal to Date of Joining,La date de libération doit être supérieure ou égale à la date d'adhésion, +Relieving Date must be greater than or equal to Date of Joining,La date de libération doit être supérieure ou égale à la date d'adhésion, Rename,Renommer, Rename Not Allowed,Renommer non autorisé, Repayment Method is mandatory for term loans,La méthode de remboursement est obligatoire pour les prêts à terme, @@ -4090,70 +4090,70 @@ Report Item,Élément de rapport, Report this Item,Signaler cet article, Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Quantité réservée pour la sous-traitance: quantité de matières premières pour fabriquer des articles sous-traités., Reset,Réinitialiser, -Reset Service Level Agreement,Réinitialiser l'accord de niveau de service, -Resetting Service Level Agreement.,Réinitialisation de l'accord de niveau de service., -Response Time for {0} at index {1} can't be greater than Resolution Time.,Le temps de réponse pour {0} à l'index {1} ne peut pas être supérieur au temps de résolution., +Reset Service Level Agreement,Réinitialiser l'accord de niveau de service, +Resetting Service Level Agreement.,Réinitialisation de l'accord de niveau de service., +Response Time for {0} at index {1} can't be greater than Resolution Time.,Le temps de réponse pour {0} à l'index {1} ne peut pas être supérieur au temps de résolution., Return amount cannot be greater unclaimed amount,Le montant du retour ne peut pas être supérieur au montant non réclamé, Review,La revue, Room,Chambre, Room Type,Type de chambre, Row # ,Ligne #, -Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Ligne # {0}: l'entrepôt accepté et l'entrepôt fournisseur ne peuvent pas être identiques, -Row #{0}: Cannot delete item {1} which has already been billed.,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été facturé., -Row #{0}: Cannot delete item {1} which has already been delivered,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été livré, -Row #{0}: Cannot delete item {1} which has already been received,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été reçu, -Row #{0}: Cannot delete item {1} which has work order assigned to it.,Ligne # {0}: impossible de supprimer l'élément {1} auquel un bon de travail est affecté., -Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,Ligne # {0}: impossible de supprimer l'article {1} affecté à la commande d'achat du client., -Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,Ligne # {0}: Impossible de sélectionner l'entrepôt fournisseur lors de la fourniture de matières premières au sous-traitant, -Row #{0}: Cost Center {1} does not belong to company {2},Ligne # {0}: le centre de coûts {1} n'appartient pas à l'entreprise {2}, -Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Ligne n ° {0}: l'opération {1} n'est pas terminée pour {2} quantité de produits finis dans l'ordre de travail {3}. Veuillez mettre à jour le statut de l'opération via la carte de travail {4}., +Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Ligne # {0}: l'entrepôt accepté et l'entrepôt fournisseur ne peuvent pas être identiques, +Row #{0}: Cannot delete item {1} which has already been billed.,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été facturé., +Row #{0}: Cannot delete item {1} which has already been delivered,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été livré, +Row #{0}: Cannot delete item {1} which has already been received,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été reçu, +Row #{0}: Cannot delete item {1} which has work order assigned to it.,Ligne # {0}: impossible de supprimer l'élément {1} auquel un bon de travail est affecté., +Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,Ligne # {0}: impossible de supprimer l'article {1} affecté à la commande d'achat du client., +Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,Ligne # {0}: Impossible de sélectionner l'entrepôt fournisseur lors de la fourniture de matières premières au sous-traitant, +Row #{0}: Cost Center {1} does not belong to company {2},Ligne # {0}: le centre de coûts {1} n'appartient pas à l'entreprise {2}, +Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Ligne n ° {0}: l'opération {1} n'est pas terminée pour {2} quantité de produits finis dans l'ordre de travail {3}. Veuillez mettre à jour le statut de l'opération via la carte de travail {4}., Row #{0}: Payment document is required to complete the transaction,Ligne n ° {0}: Un document de paiement est requis pour effectuer la transaction., -Row #{0}: Serial No {1} does not belong to Batch {2},Ligne # {0}: le numéro de série {1} n'appartient pas au lot {2}, +Row #{0}: Serial No {1} does not belong to Batch {2},Ligne # {0}: le numéro de série {1} n'appartient pas au lot {2}, Row #{0}: Service End Date cannot be before Invoice Posting Date,Ligne # {0}: la date de fin du service ne peut pas être antérieure à la date de validation de la facture, Row #{0}: Service Start Date cannot be greater than Service End Date,Ligne # {0}: la date de début du service ne peut pas être supérieure à la date de fin du service, Row #{0}: Service Start and End Date is required for deferred accounting,Ligne # {0}: la date de début et de fin du service est requise pour la comptabilité différée, -Row {0}: Invalid Item Tax Template for item {1},Ligne {0}: modèle de taxe sur les articles non valide pour l'article {1}, -Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3}),Ligne {0}: quantité non disponible pour {4} dans l'entrepôt {1} au moment de la comptabilisation de l'entrée ({2} {3})., -Row {0}: user has not applied the rule {1} on the item {2},Ligne {0}: l'utilisateur n'a pas appliqué la règle {1} sur l'élément {2}, -Row {0}:Sibling Date of Birth cannot be greater than today.,Ligne {0}: la date de naissance du frère ou de la sœur ne peut pas être supérieure à celle d'aujourd'hui., +Row {0}: Invalid Item Tax Template for item {1},Ligne {0}: modèle de taxe sur les articles non valide pour l'article {1}, +Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3}),Ligne {0}: quantité non disponible pour {4} dans l'entrepôt {1} au moment de la comptabilisation de l'entrée ({2} {3})., +Row {0}: user has not applied the rule {1} on the item {2},Ligne {0}: l'utilisateur n'a pas appliqué la règle {1} sur l'élément {2}, +Row {0}:Sibling Date of Birth cannot be greater than today.,Ligne {0}: la date de naissance du frère ou de la sœur ne peut pas être supérieure à celle d'aujourd'hui., Row({0}): {1} is already discounted in {2},Ligne ({0}): {1} est déjà réduit dans {2}., Rows Added in {0},Lignes ajoutées dans {0}, Rows Removed in {0},Lignes supprimées dans {0}, Sanctioned Amount limit crossed for {0} {1},Montant sanctionné dépassé pour {0} {1}, -Sanctioned Loan Amount already exists for {0} against company {1},Le montant du prêt sanctionné existe déjà pour {0} contre l'entreprise {1}, +Sanctioned Loan Amount already exists for {0} against company {1},Le montant du prêt sanctionné existe déjà pour {0} contre l'entreprise {1}, Save,sauvegarder, -Save Item,Enregistrer l'élément, +Save Item,Enregistrer l'élément, Saved Items,Articles sauvegardés, -Scheduled and Admitted dates can not be less than today,Les dates prévues et admises ne peuvent être inférieures à celles d'aujourd'hui, +Scheduled and Admitted dates can not be less than today,Les dates prévues et admises ne peuvent être inférieures à celles d'aujourd'hui, Search Items ...,Rechercher des articles ..., Search for a payment,Rechercher un paiement, -Search for anything ...,Rechercher n'importe quoi ..., +Search for anything ...,Rechercher n'importe quoi ..., Search results for,Résultats de recherche pour, Select All,Sélectionner Tout, Select Difference Account,Sélectionnez compte différentiel, Select a Default Priority.,Sélectionnez une priorité par défaut., Select a Supplier from the Default Supplier List of the items below.,Sélectionnez un fournisseur dans la liste des fournisseurs par défaut des éléments ci-dessous., Select a company,Sélectionnez une entreprise, -Select finance book for the item {0} at row {1},Sélectionnez le livre de financement pour l'élément {0} à la ligne {1}., +Select finance book for the item {0} at row {1},Sélectionnez le livre de financement pour l'élément {0} à la ligne {1}., Select only one Priority as Default.,Sélectionnez une seule priorité par défaut., Seller Information,Information du vendeur, Send,Envoyer, Send a message,Envoyer un message, Sending,Envoi, -Sends Mails to lead or contact based on a Campaign schedule,Envoie des courriers à diriger ou à contacter en fonction d'un calendrier de campagne, +Sends Mails to lead or contact based on a Campaign schedule,Envoie des courriers à diriger ou à contacter en fonction d'un calendrier de campagne, Serial Number Created,Numéro de série créé, Serial Numbers Created,Numéros de série créés, -Serial no(s) required for serialized item {0},N ° de série requis pour l'article sérialisé {0}, +Serial no(s) required for serialized item {0},N ° de série requis pour l'article sérialisé {0}, Series,Séries, Server Error,erreur du serveur, -Service Level Agreement has been changed to {0}.,L'accord de niveau de service a été remplacé par {0}., -Service Level Agreement tracking is not enabled.,Le suivi des accords de niveau de service n'est pas activé., -Service Level Agreement was reset.,L'accord de niveau de service a été réinitialisé., -Service Level Agreement with Entity Type {0} and Entity {1} already exists.,L'accord de niveau de service avec le type d'entité {0} et l'entité {1} existe déjà., +Service Level Agreement has been changed to {0}.,L'accord de niveau de service a été remplacé par {0}., +Service Level Agreement tracking is not enabled.,Le suivi des accords de niveau de service n'est pas activé., +Service Level Agreement was reset.,L'accord de niveau de service a été réinitialisé., +Service Level Agreement with Entity Type {0} and Entity {1} already exists.,L'accord de niveau de service avec le type d'entité {0} et l'entité {1} existe déjà., Set,Définir, Set Meta Tags,Définir les balises méta, -Set Response Time and Resolution for Priority {0} at index {1}.,Définissez le temps de réponse et la résolution pour la priorité {0} à l'index {1}., -Set {0} in company {1},Définissez {0} dans l'entreprise {1}, +Set Response Time and Resolution for Priority {0} at index {1}.,Définissez le temps de réponse et la résolution pour la priorité {0} à l'index {1}., +Set {0} in company {1},Définissez {0} dans l'entreprise {1}, Setup,Configuration, Setup Wizard,Assistant de configuration, Shift Management,Gestion des quarts, @@ -4163,10 +4163,10 @@ Show Sales Person,Afficher le vendeur, Show Stock Ageing Data,Afficher les données sur le vieillissement des stocks, Show Warehouse-wise Stock,Afficher le stock entre les magasins, Size,Taille, -Something went wrong while evaluating the quiz.,Quelque chose s'est mal passé lors de l'évaluation du quiz., +Something went wrong while evaluating the quiz.,Quelque chose s'est mal passé lors de l'évaluation du quiz., "Sorry,coupon code are exhausted","Désolé, le code de coupon est épuisé", "Sorry,coupon code validity has expired","Désolé, la validité du code promo a expiré", -"Sorry,coupon code validity has not started","Désolé, la validité du code promo n'a pas commencé", +"Sorry,coupon code validity has not started","Désolé, la validité du code promo n'a pas commencé", Sr,Sr, Start,Démarrer, Start Date cannot be before the current date,La date de début ne peut pas être antérieure à la date du jour, @@ -4178,32 +4178,32 @@ Stock Entry has been already created against this Pick List,Une entrée de stock Stock Ledger ID,ID du registre des stocks, Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.,La valeur du stock ({0}) et le solde du compte ({1}) ne sont pas synchronisés pour le compte {2} et ses entrepôts liés., Stores - {0},Magasins - {0}, -Student with email {0} does not exist,Étudiant avec le courrier électronique {0} n'existe pas, +Student with email {0} does not exist,Étudiant avec le courrier électronique {0} n'existe pas, Submit Review,Poster un commentaire, Submitted,Soumis, Supplier Addresses And Contacts,Adresses et contacts des fournisseurs, Synchronize this account,Synchroniser ce compte, Tag,Étiquette, -Target Location is required while receiving Asset {0} from an employee,L'emplacement cible est requis lors de la réception de l'élément {0} d'un employé, -Target Location is required while transferring Asset {0},L'emplacement cible est requis lors du transfert de l'élément {0}, -Target Location or To Employee is required while receiving Asset {0},L'emplacement cible ou l'employé est requis lors de la réception de l'élément {0}, +Target Location is required while receiving Asset {0} from an employee,L'emplacement cible est requis lors de la réception de l'élément {0} d'un employé, +Target Location is required while transferring Asset {0},L'emplacement cible est requis lors du transfert de l'élément {0}, +Target Location or To Employee is required while receiving Asset {0},L'emplacement cible ou l'employé est requis lors de la réception de l'élément {0}, Task's {0} End Date cannot be after Project's End Date.,La date de fin {0} de la tâche ne peut pas être postérieure à la date de fin du projet., Task's {0} Start Date cannot be after Project's End Date.,La date de début {0} de la tâche ne peut pas être postérieure à la date de fin du projet., Tax Account not specified for Shopify Tax {0},Compte de taxe non spécifié pour Shopify Tax {0}, Tax Total,Total de la taxe, Template,Modèle, -The Campaign '{0}' already exists for the {1} '{2}',La campagne '{0}' existe déjà pour le {1} '{2}'., +The Campaign '{0}' already exists for the {1} '{2}',La campagne '{0}' existe déjà pour le {1} '{2}'., The difference between from time and To Time must be a multiple of Appointment,La différence entre from time et To Time doit être un multiple de Appointment, -The field Asset Account cannot be blank,Le champ Compte d'actif ne peut pas être vide, +The field Asset Account cannot be blank,Le champ Compte d'actif ne peut pas être vide, The field Equity/Liability Account cannot be blank,Le champ Compte d’équité / de responsabilité ne peut pas être vide, The following serial numbers were created:

    {0},Les numéros de série suivants ont été créés:

    {0}, -The parent account {0} does not exists in the uploaded template,Le compte parent {0} n'existe pas dans le modèle téléchargé, +The parent account {0} does not exists in the uploaded template,Le compte parent {0} n'existe pas dans le modèle téléchargé, The question cannot be duplicate,La question ne peut pas être dupliquée, -The selected payment entry should be linked with a creditor bank transaction,L'entrée de paiement sélectionnée doit être liée à une transaction bancaire créditrice, -The selected payment entry should be linked with a debtor bank transaction,L'entrée de paiement sélectionnée doit être liée à une transaction bancaire débitrice, +The selected payment entry should be linked with a creditor bank transaction,L'entrée de paiement sélectionnée doit être liée à une transaction bancaire créditrice, +The selected payment entry should be linked with a debtor bank transaction,L'entrée de paiement sélectionnée doit être liée à une transaction bancaire débitrice, The total allocated amount ({0}) is greated than the paid amount ({1}).,Le montant total alloué ({0}) est supérieur au montant payé ({1})., The value {0} is already assigned to an exisiting Item {2}.,La valeur {0} est déjà attribuée à un élément existant {2}., -There are no vacancies under staffing plan {0},Il n'y a pas de postes vacants dans le plan de dotation en personnel {0}, +There are no vacancies under staffing plan {0},Il n'y a pas de postes vacants dans le plan de dotation en personnel {0}, This Service Level Agreement is specific to Customer {0},Cet accord de niveau de service est spécifique au client {0}., This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?,Cette action dissociera ce compte de tout service externe intégrant ERPNext avec vos comptes bancaires. Ça ne peut pas être défait. Êtes-vous sûr ?, This bank account is already synchronized,Ce compte bancaire est déjà synchronisé, @@ -4219,7 +4219,7 @@ Title,Titre, To date needs to be before from date,À ce jour doit être avant la date du, Total,Total, Total Early Exits,Total des sorties anticipées, -Total Late Entries,Nombre total d'entrées en retard, +Total Late Entries,Nombre total d'entrées en retard, Total Payment Request amount cannot be greater than {0} amount,Le montant total de la demande de paiement ne peut être supérieur à {0}., Total payments amount can't be greater than {},Le montant total des paiements ne peut être supérieur à {}, Totals,Totaux, @@ -4230,8 +4230,8 @@ Transport Receipt No and Date are mandatory for your chosen Mode of Transport,Le Tuesday,Mardi, Type,Type, Unable to find Salary Component {0},Impossible de trouver la composante salaire {0}, -Unable to find the time slot in the next {0} days for the operation {1}.,Impossible de trouver l'intervalle de temps dans les {0} jours suivants pour l'opération {1}., -Unable to update remote activity,Impossible de mettre à jour l'activité à distance, +Unable to find the time slot in the next {0} days for the operation {1}.,Impossible de trouver l'intervalle de temps dans les {0} jours suivants pour l'opération {1}., +Unable to update remote activity,Impossible de mettre à jour l'activité à distance, Unknown Caller,Appelant inconnu, Unlink external integrations,Dissocier les intégrations externes, Unmarked Attendance for days,Présence non marquée pendant des jours, @@ -4241,7 +4241,7 @@ Unsupported GST Category for E-Way Bill JSON generation,Catégorie GST non prise Update,Mettre à Jour, Update Details,Détails de mise à jour, Update Taxes for Items,Mettre à jour les taxes pour les articles, -"Upload a bank statement, link or reconcile a bank account","Télécharger un relevé bancaire, un lien ou un rapprochement d'un compte bancaire", +"Upload a bank statement, link or reconcile a bank account","Télécharger un relevé bancaire, un lien ou un rapprochement d'un compte bancaire", Upload a statement,Télécharger une déclaration, Use a name that is different from previous project name,Utilisez un nom différent du nom du projet précédent, User {0} is disabled,Utilisateur {0} est désactivé, @@ -4249,7 +4249,7 @@ Users and Permissions,Utilisateurs et autorisations, Vacancies cannot be lower than the current openings,Les postes vacants ne peuvent pas être inférieurs aux ouvertures actuelles, Valid From Time must be lesser than Valid Upto Time.,La période de validité doit être inférieure à la durée de validité., Valuation Rate required for Item {0} at row {1},Taux de valorisation requis pour le poste {0} à la ligne {1}, -"Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting / cancelling this entry.","Taux de valorisation non trouvé pour le poste {0}, nécessaire pour effectuer les écritures comptables pour {1} {2}. Si l'élément effectue une transaction en tant qu'élément à taux d'évaluation zéro dans {1}, veuillez l'indiquer dans le tableau {1} Article. Sinon, créez une transaction de stock entrante pour l'article ou indiquez le taux de valorisation dans l'enregistrement de l'article, puis essayez de soumettre / annuler cette entrée.", +"Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting / cancelling this entry.","Taux de valorisation non trouvé pour le poste {0}, nécessaire pour effectuer les écritures comptables pour {1} {2}. Si l'élément effectue une transaction en tant qu'élément à taux d'évaluation zéro dans {1}, veuillez l'indiquer dans le tableau {1} Article. Sinon, créez une transaction de stock entrante pour l'article ou indiquez le taux de valorisation dans l'enregistrement de l'article, puis essayez de soumettre / annuler cette entrée.", Values Out Of Sync,Valeurs désynchronisées, Vehicle Type is required if Mode of Transport is Road,Le type de véhicule est requis si le mode de transport est la route, Vendor Name,Nom du vendeur, @@ -4261,21 +4261,21 @@ Warehouse,Entrepôt, Warehouse not found against the account {0},Entrepôt introuvable sur le compte {0}, Welcome to {0},Bienvenue sur {0}, Why do think this Item should be removed?,Pourquoi pensez-vous que cet élément devrait être supprimé?, -Work Order {0}: Job Card not found for the operation {1},Bon de travail {0}: carte de travail non trouvée pour l'opération {1}, +Work Order {0}: Job Card not found for the operation {1},Bon de travail {0}: carte de travail non trouvée pour l'opération {1}, Workday {0} has been repeated.,La journée de travail {0} a été répétée., XML Files Processed,Fichiers XML traités, Year,Année, Yearly,Annuel, You,Vous, -You are not allowed to enroll for this course,Vous n'êtes pas autorisé à vous inscrire à ce cours, -You are not enrolled in program {0},Vous n'êtes pas inscrit au programme {0}, -You can Feature upto 8 items.,Vous pouvez présenter jusqu'à 8 éléments., +You are not allowed to enroll for this course,Vous n'êtes pas autorisé à vous inscrire à ce cours, +You are not enrolled in program {0},Vous n'êtes pas inscrit au programme {0}, +You can Feature upto 8 items.,Vous pouvez présenter jusqu'à 8 éléments., You can also copy-paste this link in your browser,Vous pouvez également copier-coller ce lien dans votre navigateur, -You can publish upto 200 items.,Vous pouvez publier jusqu'à 200 articles., -You can't create accounting entries in the closed accounting period {0},Vous ne pouvez pas créer d'écritures comptables dans la période comptable clôturée {0}., +You can publish upto 200 items.,Vous pouvez publier jusqu'à 200 articles., +You can't create accounting entries in the closed accounting period {0},Vous ne pouvez pas créer d'écritures comptables dans la période comptable clôturée {0}., You have to enable auto re-order in Stock Settings to maintain re-order levels.,Vous devez activer la re-commande automatique dans les paramètres de stock pour maintenir les niveaux de ré-commande., You must be a registered supplier to generate e-Way Bill,Vous devez être un fournisseur enregistré pour générer une facture électronique, -You need to login as a Marketplace User before you can add any reviews.,Vous devez vous connecter en tant qu'utilisateur de la Marketplace avant de pouvoir ajouter des critiques., +You need to login as a Marketplace User before you can add any reviews.,Vous devez vous connecter en tant qu'utilisateur de la Marketplace avant de pouvoir ajouter des critiques., Your Featured Items,Vos articles en vedette, Your Items,Vos articles, Your Profile,Votre profil, @@ -4291,9 +4291,9 @@ woocommerce - {0},woocommerce - {0}, {0} bank transaction(s) created and {1} errors,{0} transaction (s) bancaire (s) créée (s) et {1} erreur (s), {0} can not be greater than {1},{0} ne peut pas être supérieur à {1}, {0} conversations,{0} conversations, -{0} is not a company bank account,{0} n'est pas un compte bancaire d'entreprise, -{0} is not a group node. Please select a group node as parent cost center,{0} n'est pas un nœud de groupe. Veuillez sélectionner un nœud de groupe comme centre de coûts parent, -{0} is not the default supplier for any items.,{0} n'est le fournisseur par défaut d'aucun élément., +{0} is not a company bank account,{0} n'est pas un compte bancaire d'entreprise, +{0} is not a group node. Please select a group node as parent cost center,{0} n'est pas un nœud de groupe. Veuillez sélectionner un nœud de groupe comme centre de coûts parent, +{0} is not the default supplier for any items.,{0} n'est le fournisseur par défaut d'aucun élément., {0} is required,{0} est nécessaire, {0} units of {1} is not available.,{0} unités de {1} ne sont pas disponibles., {0}: {1} must be less than {2},{0}: {1} doit être inférieur à {2}, @@ -4329,7 +4329,7 @@ Loan Amount is mandatory,Le montant du prêt est obligatoire, Mode Of Payment,Mode de Paiement, No students Found,Aucun étudiant trouvé, Not in Stock,En Rupture de Stock, -Please select a Customer,S'il vous plaît sélectionner un client, +Please select a Customer,S'il vous plaît sélectionner un client, Printed On,Imprimé sur, Received From,Reçu de, Sales Person,Vendeur, @@ -4339,7 +4339,7 @@ Write Off,Reprise, Email Id,Identifiant Email, No,Non, Reference Doctype,DocType de la Référence, -User Id,Identifiant d'utilisateur, +User Id,Identifiant d'utilisateur, Yes,Oui, Actual ,Réel, Add to cart,Ajouter au Panier, @@ -4370,7 +4370,7 @@ Open Projects ,Projets ouverts, Open To Do ,ToDo ouvertes, Operation Id,ID de l'Opération, Partially ordered,Partiellement Ordonné, -Please select company first,Sélectionnez d'abord l'entreprise, +Please select company first,Sélectionnez d'abord l'entreprise, Please select patient,Veuillez sélectionner un patient, Printed On ,Imprimé sur, Projected qty,Qté Projetée, @@ -4378,7 +4378,7 @@ Sales person,Vendeur, Serial No {0} Created,N° de Série {0} créé, Set as default,Définir par défaut, Source Location is required for the Asset {0},La localisation source est requis pour l'actif {0}, -Tax Id,Numéro d'identification fiscale, +Tax Id,Numéro d'identification fiscale, To Time,Horaire de Fin, To date cannot be before from date,À ce jour ne peut pas être antérieure à la date du, Total Taxable value,Valeur taxable totale, @@ -4394,19 +4394,19 @@ to,à, Cards,Cartes, Percentage,Pourcentage, Failed to setup defaults for country {0}. Please contact support@erpnext.com,Échec de la configuration des paramètres par défaut pour le pays {0}. Veuillez contacter support@erpnext.com, -Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Ligne # {0}: l'article {1} n'est pas un article sérialisé / en lot. Il ne peut pas avoir de numéro de série / de lot contre lui., +Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Ligne # {0}: l'article {1} n'est pas un article sérialisé / en lot. Il ne peut pas avoir de numéro de série / de lot contre lui., Please set {0},Veuillez définir {0}, Please set {0},Veuillez définir {0},supplier Draft,Brouillon,"docstatus,=,0" Cancelled,Annulé,"docstatus,=,2" -Please setup Instructor Naming System in Education > Education Settings,Veuillez configurer le système de dénomination de l'instructeur dans Éducation> Paramètres de l'éducation, +Please setup Instructor Naming System in Education > Education Settings,Veuillez configurer le système de dénomination de l'instructeur dans Éducation> Paramètres de l'éducation, Please set Naming Series for {0} via Setup > Settings > Naming Series,Veuillez définir la série de noms pour {0} via Configuration> Paramètres> Série de noms, -UOM Conversion factor ({0} -> {1}) not found for item: {2},Facteur de conversion UdM ({0} -> {1}) introuvable pour l'article: {2}, -Item Code > Item Group > Brand,Code article> Groupe d'articles> Marque, +UOM Conversion factor ({0} -> {1}) not found for item: {2},Facteur de conversion UdM ({0} -> {1}) introuvable pour l'article: {2}, +Item Code > Item Group > Brand,Code article> Groupe d'articles> Marque, Customer > Customer Group > Territory,Client> Groupe de clients> Territoire, Supplier > Supplier Type,Fournisseur> Type de fournisseur, Please setup Employee Naming System in Human Resource > HR Settings,Veuillez configurer le système de dénomination des employés dans Ressources humaines> Paramètres RH, -Please setup numbering series for Attendance via Setup > Numbering Series,Veuillez configurer la série de numérotation pour l'assistance via Configuration> Série de numérotation, +Please setup numbering series for Attendance via Setup > Numbering Series,Veuillez configurer la série de numérotation pour l'assistance via Configuration> Série de numérotation, Purchase Order Required,Bon de Commande Requis, Purchase Receipt Required,Reçu d’Achat Requis, Requested,Demandé, @@ -4462,7 +4462,7 @@ Accounts Frozen Upto,Comptes Gelés Jusqu'au, "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.","Les écritures comptables sont gelées jusqu'à cette date, personne ne peut ajouter / modifier les entrées sauf les rôles spécifiés ci-dessous.", Role Allowed to Set Frozen Accounts & Edit Frozen Entries,Rôle Autorisé à Geler des Comptes & à Éditer des Écritures Gelées, Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Les utilisateurs ayant ce rôle sont autorisés à définir les comptes gelés et à créer / modifier des écritures comptables sur des comptes gelés, -Determine Address Tax Category From,Déterminer la catégorie de taxe d'adresse à partir de, +Determine Address Tax Category From,Déterminer la catégorie de taxe d'adresse à partir de, Address used to determine Tax Category in transactions.,Adresse utilisée pour déterminer la catégorie de taxe dans les transactions., Over Billing Allowance (%),Frais de facturation excédentaires (%), Percentage you are allowed to bill more against the amount ordered. For example: If the order value is $100 for an item and tolerance is set as 10% then you are allowed to bill for $110.,"Pourcentage vous êtes autorisé à facturer plus par rapport au montant commandé. Par exemple: Si la valeur de la commande est de 100 USD pour un article et que la tolérance est définie sur 10%, vous êtes autorisé à facturer 110 USD.", @@ -4471,14 +4471,14 @@ Role that is allowed to submit transactions that exceed credit limits set.,Rôle Check Supplier Invoice Number Uniqueness,Vérifiez l'Unicité du Numéro de Facture du Fournisseur, Make Payment via Journal Entry,Effectuer un Paiement par une Écriture de Journal, Unlink Payment on Cancellation of Invoice,Délier Paiement à l'Annulation de la Facture, -Unlink Advance Payment on Cancelation of Order,Dissocier le paiement anticipé lors de l'annulation d'une commande, +Unlink Advance Payment on Cancelation of Order,Dissocier le paiement anticipé lors de l'annulation d'une commande, Book Asset Depreciation Entry Automatically,Comptabiliser les Entrées de Dépréciation d'Actifs Automatiquement, Allow Cost Center In Entry of Balance Sheet Account,Autoriser le centre de coûts en saisie du compte de bilan, Automatically Add Taxes and Charges from Item Tax Template,Ajouter automatiquement des taxes et des frais à partir du modèle de taxe à la pièce, Automatically Fetch Payment Terms,Récupérer automatiquement les conditions de paiement, Show Inclusive Tax In Print,Afficher la taxe inclusive en impression, Show Payment Schedule in Print,Afficher le calendrier de paiement dans Imprimer, -Currency Exchange Settings,Paramètres d'échange de devises, +Currency Exchange Settings,Paramètres d'échange de devises, Allow Stale Exchange Rates,Autoriser les Taux de Change Existants, Stale Days,Journées Passées, Report Settings,Paramètres de rapport, @@ -4489,20 +4489,20 @@ Branch Code,Code de la branche, Address and Contact,Adresse et Contact, Address HTML,Adresse HTML, Contact HTML,HTML du Contact, -Data Import Configuration,Configuration de l'importation de données, +Data Import Configuration,Configuration de l'importation de données, Bank Transaction Mapping,Cartographie des transactions bancaires, -Plaid Access Token,Jeton d'accès plaid, -Company Account,Compte d'entreprise, +Plaid Access Token,Jeton d'accès plaid, +Company Account,Compte d'entreprise, Account Subtype,Sous-type de compte, Is Default Account,Est un compte par défaut, -Is Company Account,Est le compte de l'entreprise, +Is Company Account,Est le compte de l'entreprise, Party Details,Parti Détails, Account Details,Détails du compte, IBAN,IBAN, Bank Account No,No de compte bancaire, -Integration Details,Détails d'intégration, -Integration ID,ID d'intégration, -Last Integration Date,Dernière date d'intégration, +Integration Details,Détails d'intégration, +Integration ID,ID d'intégration, +Last Integration Date,Dernière date d'intégration, Change this date manually to setup the next synchronization start date,Modifiez cette date manuellement pour définir la prochaine date de début de la synchronisation., Mask,Masque, Bank Guarantee,Garantie Bancaire, @@ -4609,7 +4609,7 @@ POS-CLO-,POS-CLO-, Custody,Garde, Net Amount,Montant Net, Cashier Closing Payments,Paiements de clôture du caissier, -Import Chart of Accounts from a csv file,Importer un tableau de comptes à partir d'un fichier csv, +Import Chart of Accounts from a csv file,Importer un tableau de comptes à partir d'un fichier csv, Attach custom Chart of Accounts file,Joindre un fichier de plan comptable personnalisé, Chart Preview,Aperçu du graphique, Chart Tree,Arbre à cartes, @@ -4641,7 +4641,7 @@ lft,Lft, rgt,rgt, Coupon Code,Code de coupon, Coupon Name,Nom du coupon, -"e.g. ""Summer Holiday 2019 Offer 20""",ex. "Offre vacances d'été 2019 20", +"e.g. ""Summer Holiday 2019 Offer 20""",ex. "Offre vacances d'été 2019 20", Coupon Type,Type de coupon, Promotional,Promotionnel, Gift Card,Carte cadeau, @@ -4692,9 +4692,9 @@ Bank Charges Account,Compte de frais bancaires, Accounts Receivable Credit Account,Compte de crédit débiteur, Accounts Receivable Discounted Account,Compte escompté des comptes débiteurs, Accounts Receivable Unpaid Account,Comptes débiteurs non payés, -Item Tax Template,Modèle de taxe d'article, -Tax Rates,Les taux d'imposition, -Item Tax Template Detail,Détail du modèle de taxe d'article, +Item Tax Template,Modèle de taxe d'article, +Tax Rates,Les taux d'imposition, +Item Tax Template Detail,Détail du modèle de taxe d'article, Entry Type,Type d'Écriture, Inter Company Journal Entry,Ecriture de journal inter-sociétés, Bank Entry,Écriture Bancaire, @@ -4730,7 +4730,7 @@ Debit in Company Currency,Débit en Devise Société, Credit in Company Currency,Crédit dans la Devise de la Société, Payroll Entry,Entrée de la paie, Employee Advance,Avance versée aux employés, -Reference Due Date,Date d'échéance de référence, +Reference Due Date,Date d'échéance de référence, Loyalty Program Tier,Echelon de programme de fidélité, Redeem Against,Échanger contre, Expiry Date,Date d'expiration, @@ -4748,8 +4748,8 @@ Collection Rules,Règles de collecte, Redemption,Echange, Conversion Factor,Facteur de Conversion, 1 Loyalty Points = How much base currency?,1 point de fidélité = Quel montant en devise de base ?, -Expiry Duration (in days),Durée d'expiration (en jours), -Help Section,Section d'aide, +Expiry Duration (in days),Durée d'expiration (en jours), +Help Section,Section d'aide, Loyalty Program Help,Aide au programme de fidélité, Loyalty Program Collection,Collecte du programme de fidélité, Tier Name,Nom de l'échelon, @@ -4767,17 +4767,17 @@ Monthly Distribution Percentage,Pourcentage de Répartition Mensuelle, Percentage Allocation,Allocation en Pourcentage, Create Missing Party,Créer les tiers manquants, Create missing customer or supplier.,Créer les clients ou les fournisseurs manquant, -Opening Invoice Creation Tool Item,Ouverture d'un outil de création de facture, +Opening Invoice Creation Tool Item,Ouverture d'un outil de création de facture, Temporary Opening Account,Compte temporaire d'ouverture, Party Account,Compte de Tiers, Type of Payment,Type de Paiement, ACC-PAY-.YYYY.-,ACC-PAY-YYYY.-, Receive,Recevoir, Internal Transfer,Transfert Interne, -Payment Order Status,État de l'ordre de paiement, +Payment Order Status,État de l'ordre de paiement, Payment Ordered,Paiement commandé, Payment From / To,Paiement De / À, -Company Bank Account,Compte bancaire de l'entreprise, +Company Bank Account,Compte bancaire de l'entreprise, Party Bank Account,Compte bancaire du parti, Account Paid From,Compte Payé Du, Account Paid To,Compte Payé Au, @@ -4802,8 +4802,8 @@ Payment Gateway Account,Compte Passerelle de Paiement, Payment Account,Compte de Paiement, Default Payment Request Message,Message de Demande de Paiement par Défaut, PMO-,PMO-, -Payment Order Type,Type d'ordre de paiement, -Payment Order Reference,Référence de l'ordre de paiement, +Payment Order Type,Type d'ordre de paiement, +Payment Order Reference,Référence de l'ordre de paiement, Bank Account Details,Détails de compte en banque, Payment Reconciliation,Réconciliation des Paiements, Receivable / Payable Account,Compte Débiteur / Créditeur, @@ -4822,14 +4822,14 @@ Payment Reconciliation Payment,Paiement de Réconciliation des Paiements, Reference Row,Ligne de Référence, Allocated amount,Montant alloué, Payment Request Type,Type de demande de paiement, -Outward,À l'extérieur, -Inward,Vers l'intérieur, +Outward,À l'extérieur, +Inward,Vers l'intérieur, ACC-PRQ-.YYYY.-,ACC-PRQ-.AAAA.-, Transaction Details,détails de la transaction, Amount in customer's currency,Montant dans la devise du client, Is a Subscription,Est un abonnement, Transaction Currency,Devise de la Transaction, -Subscription Plans,Plans d'abonnement, +Subscription Plans,Plans d'abonnement, SWIFT Number,Numéro SWIFT, Recipient Message And Payment Details,Message du Destinataire et Détails de Paiement, Make Sales Invoice,Faire des Factures de Vente, @@ -4840,7 +4840,7 @@ Payment Schedule,Calendrier de paiement, Invoice Portion,Pourcentage de facturation, Payment Amount,Montant du paiement, Payment Term Name,Nom du terme de paiement, -Due Date Based On,Date d'échéance basée sur, +Due Date Based On,Date d'échéance basée sur, Day(s) after invoice date,Jour (s) après la date de la facture, Day(s) after the end of the invoice month,Jour (s) après la fin du mois de facture, Month(s) after the end of the invoice month,Mois (s) après la fin du mois de la facture, @@ -4859,12 +4859,12 @@ Update Stock,Mettre à Jour le Stock, Ignore Pricing Rule,Ignorez Règle de Prix, Allow user to edit Rate,Autoriser l'utilisateur à modifier le Taux, Allow user to edit Discount,Autoriser l'utilisateur à modifier la remise, -Allow Print Before Pay,Autoriser l'impression avant la paie, +Allow Print Before Pay,Autoriser l'impression avant la paie, Display Items In Stock,Afficher les articles en stock, Applicable for Users,Applicable aux Utilisateurs, Sales Invoice Payment,Paiement de la Facture de Vente, -Item Groups,Groupes d'articles, -Only show Items from these Item Groups,Afficher uniquement les éléments de ces groupes d'éléments, +Item Groups,Groupes d'articles, +Only show Items from these Item Groups,Afficher uniquement les éléments de ces groupes d'éléments, Customer Groups,Groupes de Clients, Only show Customer of these Customer Groups,Afficher uniquement les clients de ces groupes de clients, Print Format for Online,Format d'Impression en Ligne, @@ -4878,8 +4878,8 @@ POS Profile User,Utilisateur du profil PDV, Use POS in Offline Mode,Utiliser PDV en Mode Hors-Ligne, Apply On,Appliquer Sur, Price or Product Discount,Prix ou remise de produit, -Apply Rule On Item Code,Appliquer la règle sur le code d'article, -Apply Rule On Item Group,Appliquer une règle sur un groupe d'articles, +Apply Rule On Item Code,Appliquer la règle sur le code d'article, +Apply Rule On Item Group,Appliquer une règle sur un groupe d'articles, Apply Rule On Brand,Appliquer la règle sur la marque, Mixed Conditions,Conditions mixtes, Conditions will be applied on all the selected items combined. ,Des conditions seront appliquées sur tous les éléments sélectionnés combinés., @@ -4906,7 +4906,7 @@ Product Discount Scheme,Schéma de remise de produit, Same Item,Même article, Free Item,Article gratuit, Threshold for Suggestion,Seuil de suggestion, -System will notify to increase or decrease quantity or amount ,Le système notifiera d'augmenter ou de diminuer la quantité ou le montant, +System will notify to increase or decrease quantity or amount ,Le système notifiera d'augmenter ou de diminuer la quantité ou le montant, "Higher the number, higher the priority","Plus le nombre est grand, plus la priorité est haute", Apply Multiple Pricing Rules,Appliquer plusieurs règles de tarification, Apply Discount on Rate,Appliquer une réduction sur le taux, @@ -4917,7 +4917,7 @@ Promotional Scheme Id,Id de schéma promotionnel, Promotional Scheme,Schéma promotionnel, Pricing Rule Brand,Marque de règle de tarification, Pricing Rule Detail,Détail de la règle de tarification, -Child Docname,Nom de l'enfant, +Child Docname,Nom de l'enfant, Rule Applied,Règle appliquée, Pricing Rule Item Code,Règle de tarification Article Code, Pricing Rule Item Group,Groupe de postes de règle de tarification, @@ -4933,7 +4933,7 @@ Tax Withholding Category,Catégorie de taxation à la source, Edit Posting Date and Time,Modifier la Date et l'Heure de la Publication, Is Paid,Est Payé, Is Return (Debit Note),Est une note de débit, -Apply Tax Withholding Amount,Appliquer le montant de la retenue d'impôt, +Apply Tax Withholding Amount,Appliquer le montant de la retenue d'impôt, Accounting Dimensions ,Dimensions comptables, Supplier Invoice Details,Détails de la Facture du Fournisseur, Supplier Invoice Date,Date de la Facture du Fournisseur, @@ -5021,7 +5021,7 @@ Is Fixed Asset,Est Immobilisation, Asset Location,Localisation de l'actif, Deferred Expense,Frais différés, Deferred Expense Account,Compte de dépenses différées, -Service Stop Date,Date d'arrêt du service, +Service Stop Date,Date d'arrêt du service, Enable Deferred Expense,Activer les frais reportés, Service Start Date,Date de début du service, Service End Date,Date de fin du service, @@ -5029,8 +5029,8 @@ Allow Zero Valuation Rate,Autoriser un Taux de Valorisation Égal à Zéro, Item Tax Rate,Taux de la Taxe sur l'Article, Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges,La table de détails de taxe est récupérée depuis les données de base de l'article comme une chaîne de caractères et stockée dans ce champ. Elle est utilisée pour les Taxes et Frais., Purchase Order Item,Article du Bon de Commande, -Purchase Receipt Detail,Détail du reçu d'achat, -Item Weight Details,Détails du poids de l'article, +Purchase Receipt Detail,Détail du reçu d'achat, +Item Weight Details,Détails du poids de l'article, Weight Per Unit,Poids par unité, Total Weight,Poids total, Weight UOM,UDM de Poids, @@ -5042,7 +5042,7 @@ Add or Deduct,Ajouter ou Déduire, Deduct,Déduire, On Previous Row Amount,Le Montant de la Rangée Précédente, On Previous Row Total,Le Total de la Rangée Précédente, -On Item Quantity,Sur quantité d'article, +On Item Quantity,Sur quantité d'article, Reference Row #,Ligne de Référence #, Is this Tax included in Basic Rate?,Cette Taxe est-elle incluse dans le Taux de Base ?, "If checked, the tax amount will be considered as already included in the Print Rate / Print Amount","Si cochée, le montant de la taxe sera considéré comme déjà inclus dans le Taux d'Impression / Prix d'Impression", @@ -5104,7 +5104,7 @@ Qty as per Stock UOM,Qté par UDM du Stock, Discount and Margin,Remise et Marge, Rate With Margin,Tarif Avec Marge, Discount (%) on Price List Rate with Margin,Remise (%) sur le Tarif de la Liste de Prix avec la Marge, -Rate With Margin (Company Currency),Taux avec marge (devise de l'entreprise), +Rate With Margin (Company Currency),Taux avec marge (devise de l'entreprise), Delivered By Supplier,Livré par le Fournisseur, Deferred Revenue,Produits comptabilisés d'avance, Deferred Revenue Account,Compte de produits comptabilisés d'avance, @@ -5134,7 +5134,7 @@ From Folio No,Du No de Folio, To Shareholder,A l'actionnaire, To Folio No,Au N. de Folio, Equity/Liability Account,Compte de capitaux propres / passif, -Asset Account,Compte d'actif, +Asset Account,Compte d'actif, (including),(compris), ACC-SH-.YYYY.-,ACC-SH-.YYYY.-, Folio no.,No. de Folio, @@ -5143,7 +5143,7 @@ Hidden list maintaining the list of contacts linked to Shareholder,Liste cachée Specify conditions to calculate shipping amount,Spécifier les conditions pour calculer le montant de la livraison, Shipping Rule Label,Étiquette de la Règle de Livraison, example: Next Day Shipping,Exemple : Livraison le Jour Suivant, -Shipping Rule Type,Type de règle d'expédition, +Shipping Rule Type,Type de règle d'expédition, Shipping Account,Compte de Livraison, Calculate Based On,Calculer en fonction de, Fixed,Fixé, @@ -5157,11 +5157,11 @@ A condition for a Shipping Rule,Une condition pour une Règle de Livraison, From Value,De la Valeur, To Value,Valeur Finale, Shipping Rule Country,Pays de la Règle de Livraison, -Subscription Period,Période d'abonnement, -Subscription Start Date,Date de début de l'abonnement, -Cancelation Date,Date d'annulation, -Trial Period Start Date,Date de début de la période d'essai, -Trial Period End Date,Date de fin de la période d'évaluation, +Subscription Period,Période d'abonnement, +Subscription Start Date,Date de début de l'abonnement, +Cancelation Date,Date d'annulation, +Trial Period Start Date,Date de début de la période d'essai, +Trial Period End Date,Date de fin de la période d'évaluation, Current Invoice Start Date,Date de début de la facture en cours, Current Invoice End Date,Date de fin de la facture en cours, Days Until Due,Jours avant échéance, @@ -5172,17 +5172,17 @@ Plans,Plans, Discounts,Réductions, Additional DIscount Percentage,Pourcentage de réduction supplémentaire, Additional DIscount Amount,Montant de la Remise Supplémentaire, -Subscription Invoice,Facture d'abonnement, -Subscription Plan,Plan d'abonnement, +Subscription Invoice,Facture d'abonnement, +Subscription Plan,Plan d'abonnement, Price Determination,Détermination du prix, Fixed rate,Taux fixe, Based on price list,Sur la base de la liste de prix, Cost,Coût, Billing Interval,Intervalle de facturation, -Billing Interval Count,Nombre d'intervalles de facturation, -"Number of intervals for the interval field e.g if Interval is 'Days' and Billing Interval Count is 3, invoices will be generated every 3 days","Nombre d'intervalles pour le champ d'intervalle, par exemple si Intervalle est "Jours" et si le décompte d'intervalle de facturation est 3, les factures seront générées tous les 3 jours", +Billing Interval Count,Nombre d'intervalles de facturation, +"Number of intervals for the interval field e.g if Interval is 'Days' and Billing Interval Count is 3, invoices will be generated every 3 days","Nombre d'intervalles pour le champ d'intervalle, par exemple si Intervalle est "Jours" et si le décompte d'intervalle de facturation est 3, les factures seront générées tous les 3 jours", Payment Plan,Plan de paiement, -Subscription Plan Detail,Détail du plan d'abonnement, +Subscription Plan Detail,Détail du plan d'abonnement, Plan,Plan, Subscription Settings,Paramètres des Abonnements, Grace Period,Période de grâce, @@ -5200,25 +5200,25 @@ Billing Country,Pays de Facturation, Shipping City,Ville de Livraison, Shipping County,Comté de Livraison, Shipping State,État de livraison, -Shipping Zipcode,Code postal d'expédition, +Shipping Zipcode,Code postal d'expédition, Shipping Country,Pays de Livraison, Tax Withholding Account,Compte de taxation à la source, -Tax Withholding Rates,Taux de retenue d'impôt, +Tax Withholding Rates,Taux de retenue d'impôt, Rates,Prix, -Tax Withholding Rate,Taux de retenue d'impôt, +Tax Withholding Rate,Taux de retenue d'impôt, Single Transaction Threshold,Seuil de transaction unique, Cumulative Transaction Threshold,Seuil de transaction cumulatif, -Agriculture Analysis Criteria,Critères d'analyse de l'agriculture, +Agriculture Analysis Criteria,Critères d'analyse de l'agriculture, Linked Doctype,Doctype lié, -Water Analysis,Analyse de l'eau, +Water Analysis,Analyse de l'eau, Soil Analysis,Analyse du sol, Plant Analysis,Analyse des plantes, Fertilizer,Engrais, Soil Texture,Texture du sol, Weather,Météo, -Agriculture Manager,Directeur de l'agriculture, +Agriculture Manager,Directeur de l'agriculture, Agriculture User,Agriculteur, -Agriculture Task,Tâche d'agriculture, +Agriculture Task,Tâche d'agriculture, Start Day,Date de début, End Day,Jour de fin, Holiday Management,Gestion des vacances, @@ -5231,9 +5231,9 @@ Crop Name,Nom de la culture, Scientific Name,Nom scientifique, "You can define all the tasks which need to carried out for this crop here. The day field is used to mention the day on which the task needs to be carried out, 1 being the 1st day, etc.. ","Vous pouvez définir ici toutes les tâches à effectuer pour cette culture. Le champ de jour est utilisé pour mentionner le jour où la tâche doit être effectuée, 1 étant le 1er jour, etc.", Crop Spacing,Espacement des cultures, -Crop Spacing UOM,UOM d'espacement des cultures, +Crop Spacing UOM,UOM d'espacement des cultures, Row Spacing,Écartement des rangs, -Row Spacing UOM,UOM d'espacement des lignes, +Row Spacing UOM,UOM d'espacement des lignes, Perennial,Vivace, Biennial,Biennal, Planting UOM,Unité de mesure de la plantation, @@ -5248,7 +5248,7 @@ A link to all the Locations in which the Crop is growing,Lien vers tous les lieu This will be day 1 of the crop cycle,Ce sera le jour 1 du cycle de la culture, ISO 8601 standard,Norme ISO 8601, Cycle Type,Type de cycle, -Less than a year,Moins d'un an, +Less than a year,Moins d'un an, The minimum length between each plant in the field for optimum growth,La longueur minimale entre chaque plante sur le terrain pour une croissance optimale, The minimum distance between rows of plants for optimum growth,La distance minimale entre les rangées de plantes pour une croissance optimale, Detected Diseases,Maladies détectées, @@ -5260,18 +5260,18 @@ Tasks Created,Tâches créées, Common Name,Nom commun, Treatment Task,Tâche de traitement, Treatment Period,Période de traitement, -Fertilizer Name,Nom de l'engrais, +Fertilizer Name,Nom de l'engrais, Density (if liquid),Densité (si liquide), -Fertilizer Contents,Contenu de l'engrais, -Fertilizer Content,Contenu d'engrais, +Fertilizer Contents,Contenu de l'engrais, +Fertilizer Content,Contenu d'engrais, Linked Plant Analysis,Analyse des plantes liées, Linked Soil Analysis,Analyse de sol liée, Linked Soil Texture,Texture de sol liée, Collection Datetime,Date et heure du prélèvement, Laboratory Testing Datetime,Date et heure du test de laboratoire, Result Datetime,Date et heure du résultat, -Plant Analysis Criterias,Critères d'analyse des plantes, -Plant Analysis Criteria,Critères d'analyse des plantes, +Plant Analysis Criterias,Critères d'analyse des plantes, +Plant Analysis Criteria,Critères d'analyse des plantes, Minimum Permissible Value,Valeur minimale autorisée, Maximum Permissible Value,Valeur maximale autorisée, Ca/K,Ca / K, @@ -5279,31 +5279,31 @@ Ca/Mg,Ca / Mg, Mg/K,Mg / K, (Ca+Mg)/K,(Ca + Mg) / K, Ca/(K+Ca+Mg),Ca / (K + Ca + Mg), -Soil Analysis Criterias,Critères d'analyse des sols, -Soil Analysis Criteria,Critères d'analyse des sols, +Soil Analysis Criterias,Critères d'analyse des sols, +Soil Analysis Criteria,Critères d'analyse des sols, Soil Type,Le type de sol, Loamy Sand,Sable limoneux, Sandy Loam,Limon sableux, Loam,Terreau, Silt Loam,Limon fin, Sandy Clay Loam,Limon argilo-sableux, -Clay Loam,Terreau d'argile, +Clay Loam,Terreau d'argile, Silty Clay Loam,Limon argileux fin, Sandy Clay,Argile sableuse, Silty Clay,Argile limoneuse, -Clay Composition (%),Composition d'argile (%), +Clay Composition (%),Composition d'argile (%), Sand Composition (%),Composition de sable (%), Silt Composition (%),Composition de limon (%), Ternary Plot,Tracé ternaire, Soil Texture Criteria,Critères de texture du sol, -Type of Sample,Type d'échantillon, +Type of Sample,Type d'échantillon, Container,Récipient, Origin,Origine, Collection Temperature ,Température de collecte, Storage Temperature,Température de stockage, Appearance,Apparence, Person Responsible,Personne responsable, -Water Analysis Criteria,Critères d'analyse de l'eau, +Water Analysis Criteria,Critères d'analyse de l'eau, Weather Parameter,Paramètre météo, ACC-ASS-.YYYY.-,ACC-ASS-YYYY.-, Asset Owner,Propriétaire de l'Actif, @@ -5313,7 +5313,7 @@ Disposal Date,Date d’Élimination, Journal Entry for Scrap,Écriture de Journal pour la Mise au Rebut, Available-for-use Date,Date de mise en service, Calculate Depreciation,Calculer la dépréciation, -Allow Monthly Depreciation,Autoriser l'amortissement mensuel, +Allow Monthly Depreciation,Autoriser l'amortissement mensuel, Number of Depreciations Booked,Nombre d’Amortissements Comptabilisés, Finance Books,Livres comptables, Straight Line,Linéaire, @@ -5328,17 +5328,17 @@ Depreciation Schedules,Calendriers d'Amortissement, Policy number,Numéro de politique, Insurer,Assureur, Insured value,Valeur assurée, -Insurance Start Date,Date de début de l'assurance, -Insurance End Date,Date de fin de l'assurance, +Insurance Start Date,Date de début de l'assurance, +Insurance End Date,Date de fin de l'assurance, Comprehensive Insurance,Assurance complète, Maintenance Required,Maintenance requise, -Check if Asset requires Preventive Maintenance or Calibration,Vérifier si l'actif nécessite une maintenance préventive ou un étalonnage, +Check if Asset requires Preventive Maintenance or Calibration,Vérifier si l'actif nécessite une maintenance préventive ou un étalonnage, Booked Fixed Asset,Actif immobilisé comptabilisé, -Purchase Receipt Amount,Montant du reçu d'achat, +Purchase Receipt Amount,Montant du reçu d'achat, Default Finance Book,Livre comptable par défaut, Quality Manager,Responsable Qualité, Asset Category Name,Nom de Catégorie d'Actif, -Depreciation Options,Options d'amortissement, +Depreciation Options,Options d'amortissement, Enable Capital Work in Progress Accounting,Activer la comptabilité des immobilisations en cours, Finance Book Detail,Détails du livre comptable, Asset Category Account,Compte de Catégorie d'Actif, @@ -5348,9 +5348,9 @@ Depreciation Expense Account,Compte de Dotations aux Amortissement, Capital Work In Progress Account,Compte d'immobilisation en cours, Asset Finance Book,Livre comptable d'actifs, Written Down Value,Valeur comptable nette, -Depreciation Start Date,Date de début de l'amortissement, +Depreciation Start Date,Date de début de l'amortissement, Expected Value After Useful Life,Valeur Attendue Après Utilisation Complète, -Rate of Depreciation,Taux d'amortissement, +Rate of Depreciation,Taux d'amortissement, In Percentage,En pourcentage, Select Serial No,Veuillez sélectionner le numéro de série, Maintenance Team,Équipe de maintenance, @@ -5369,43 +5369,43 @@ Preventive Maintenance,Maintenance préventive, Calibration,Étalonnage, 2 Yearly,2 ans, Certificate Required,Certificat requis, -Next Due Date,prochaine date d'échéance, -Last Completion Date,Dernière date d'achèvement, +Next Due Date,prochaine date d'échéance, +Last Completion Date,Dernière date d'achèvement, Asset Maintenance Team,Équipe de Maintenance des Actifs, -Maintenance Team Name,Nom de l'équipe de maintenance, -Maintenance Team Members,Membres de l'équipe de maintenance, +Maintenance Team Name,Nom de l'équipe de maintenance, +Maintenance Team Members,Membres de l'équipe de maintenance, Purpose,Objet, Stock Manager,Responsable des Stocks, -Asset Movement Item,Élément de mouvement d'actif, +Asset Movement Item,Élément de mouvement d'actif, Source Location,Localisation source, From Employee,De l'Employé, Target Location,Localisation cible, -To Employee,À l'employé, +To Employee,À l'employé, Asset Repair,Réparation d'Actif, ACC-ASR-.YYYY.-,ACC-ASR-.AAAA.-, -Failure Date,Date d'échec, +Failure Date,Date d'échec, Assign To Name,Attribuer au nom, Repair Status,État de réparation, Error Description,Erreur de description, -Downtime,Temps d'arrêt, +Downtime,Temps d'arrêt, Repair Cost,Coût de réparation, Manufacturing Manager,Responsable de Production, -Current Asset Value,Valeur actuelle de l'actif, -New Asset Value,Nouvelle valeur de l'actif, +Current Asset Value,Valeur actuelle de l'actif, +New Asset Value,Nouvelle valeur de l'actif, Make Depreciation Entry,Créer une Écriture d'Amortissement, Finance Book Id,Identifiant du livre comptable, Location Name,Nom du lieux, Parent Location,Localisation parente, Is Container,Est le contenant, -Check if it is a hydroponic unit,Vérifiez s'il s'agit d'une unité hydroponique, -Location Details,Détails de l'emplacement, +Check if it is a hydroponic unit,Vérifiez s'il s'agit d'une unité hydroponique, +Location Details,Détails de l'emplacement, Latitude,Latitude, Longitude,Longitude, Area,Région, Area UOM,Unité de mesure de la surface, Tree Details,Détails de l’Arbre, -Maintenance Team Member,Membre de l'équipe de maintenance, -Team Member,Membre de l'équipe, +Maintenance Team Member,Membre de l'équipe de maintenance, +Team Member,Membre de l'équipe, Maintenance Role,Rôle de maintenance, Buying Settings,Paramètres d'Achat, Settings for Buying Module,Paramètres du Module Achat, @@ -5428,7 +5428,7 @@ Customer Contact Email,Email Contact Client, Set Target Warehouse,Définir le magasin cible, Supply Raw Materials,Fournir les Matières Premières, Purchase Order Pricing Rule,Règle de tarification des bons de commande, -Set Reserve Warehouse,Définir l'entrepôt de réserve, +Set Reserve Warehouse,Définir l'entrepôt de réserve, In Words will be visible once you save the Purchase Order.,En Toutes Lettres. Sera visible une fois que vous enregistrerez le Bon de Commande., Advance Paid,Avance Payée, % Billed,% Facturé, @@ -5535,23 +5535,23 @@ Parameter Name,Nom du Paramètre, Supplier Scorecard Standing,Classement de la Fiche d'Évaluation Fournisseur, Notify Other,Notifier Autre, Supplier Scorecard Variable,Variable de la Fiche d'Évaluation Fournisseur, -Call Log,Journal d'appel, +Call Log,Journal d'appel, Received By,Reçu par, -Caller Information,Informations sur l'appelant, +Caller Information,Informations sur l'appelant, Contact Name,Nom du Contact, Lead Name,Nom du Prospect, Ringing,Sonnerie, Missed,Manqué, -Call Duration in seconds,Durée d'appel en secondes, -Recording URL,URL d'enregistrement, +Call Duration in seconds,Durée d'appel en secondes, +Recording URL,URL d'enregistrement, Communication Medium,Moyen de Communication, Communication Medium Type,Type de support de communication, Voice,Voix, Catch All,Attraper tout, -"If there is no assigned timeslot, then communication will be handled by this group","S'il n'y a pas d'intervalle de temps attribué, la communication sera gérée par ce groupe.", +"If there is no assigned timeslot, then communication will be handled by this group","S'il n'y a pas d'intervalle de temps attribué, la communication sera gérée par ce groupe.", Timeslots,Tranches de temps, Communication Medium Timeslot,Période de communication moyenne, -Employee Group,Groupe d'employés, +Employee Group,Groupe d'employés, Appointment,Rendez-Vous, Scheduled Time,Heure prévue, Unverified,Non vérifié, @@ -5563,18 +5563,18 @@ Appointment With,Rendez-vous avec, Calendar Event,Événement de calendrier, Appointment Booking Settings,Paramètres de réservation de rendez-vous, Enable Appointment Scheduling,Activer la planification des rendez-vous, -Agent Details,Détails de l'agent, +Agent Details,Détails de l'agent, Availability Of Slots,Disponibilité des emplacements, Number of Concurrent Appointments,Nombre de rendez-vous simultanés, Agents,Agents, Appointment Details,Détails du rendez-vous, Appointment Duration (In Minutes),Durée du rendez-vous (en minutes), Notify Via Email,Avertir par e-mail, -Notify customer and agent via email on the day of the appointment.,Avertissez le client et l'agent par e-mail le jour du rendez-vous., -Number of days appointments can be booked in advance,Nombre de jours de rendez-vous peuvent être réservés à l'avance, +Notify customer and agent via email on the day of the appointment.,Avertissez le client et l'agent par e-mail le jour du rendez-vous., +Number of days appointments can be booked in advance,Nombre de jours de rendez-vous peuvent être réservés à l'avance, Success Settings,Paramètres de réussite, Success Redirect URL,URL de redirection réussie, -"Leave blank for home.\nThis is relative to site URL, for example ""about"" will redirect to ""https://yoursitename.com/about""","Laissez vide pour la maison. Ceci est relatif à l'URL du site, par exemple "about" redirigera vers "https://yoursitename.com/about"", +"Leave blank for home.\nThis is relative to site URL, for example ""about"" will redirect to ""https://yoursitename.com/about""","Laissez vide pour la maison. Ceci est relatif à l'URL du site, par exemple "about" redirigera vers "https://yoursitename.com/about"", Appointment Booking Slots,Horaires de prise de rendez-vous, From Time ,Horaire de Début, Campaign Email Schedule,Calendrier des e-mails de campagne, @@ -5595,17 +5595,17 @@ Signed On,Signé le, Contract Details,Détails du contrat, Contract Template,Modèle de contrat, Contract Terms,Termes du contrat, -Fulfilment Details,Détails de l'exécution, +Fulfilment Details,Détails de l'exécution, Requires Fulfilment,Nécessite des conditions, -Fulfilment Deadline,Délai d'exécution, -Fulfilment Terms,Conditions d'exécution, -Contract Fulfilment Checklist,Liste de vérification de l'exécution des contrats, +Fulfilment Deadline,Délai d'exécution, +Fulfilment Terms,Conditions d'exécution, +Contract Fulfilment Checklist,Liste de vérification de l'exécution des contrats, Requirement,Obligations, Contract Terms and Conditions,Termes et conditions du contrat, -Fulfilment Terms and Conditions,Termes et conditions d'exécution, +Fulfilment Terms and Conditions,Termes et conditions d'exécution, Contract Template Fulfilment Terms,Conditions d'exécution du modèle de contrat, Email Campaign,Campagne Email, -Email Campaign For ,Campagne d'email pour, +Email Campaign For ,Campagne d'email pour, Lead is an Organization,Le prospect est une organisation, CRM-LEAD-.YYYY.-,CRM-LEAD-.YYYY.-, Person Name,Nom de la Personne, @@ -5668,7 +5668,7 @@ Examiner Name,Nom de l'Examinateur, Supervisor,Superviseur, Supervisor Name,Nom du Superviseur, Evaluate,Évaluer, -Maximum Assessment Score,Score d'évaluation maximale, +Maximum Assessment Score,Score d'évaluation maximale, Assessment Plan Criteria,Critères du Plan d'Évaluation, Maximum Score,Score Maximum, Total Score,Score Total, @@ -5684,10 +5684,10 @@ Course Name,Nom du Cours, Topics,Les sujets, Hero Image,Image de héros, Default Grading Scale,Échelle de Notation par Défault, -Education Manager,Gestionnaire de l'éducation, +Education Manager,Gestionnaire de l'éducation, Course Activity,Activité de cours, Course Enrollment,Inscription au cours, -Activity Date,Date d'activité, +Activity Date,Date d'activité, Course Assessment Criteria,Critères d'Évaluation du Cours, Weightage,Poids, Course Content,Le contenu des cours, @@ -5703,7 +5703,7 @@ Course End Date,Date de Fin du Cours, Course Topic,Sujet du cours, Topic,Sujet, Topic Name,Nom du Sujet, -Education Settings,Paramètres d'éducation, +Education Settings,Paramètres d'éducation, Current Academic Year,Année Académique Actuelle, Current Academic Term,Terme Académique Actuel, Attendance Freeze Date,Date du Gel des Présences, @@ -5712,7 +5712,7 @@ Validate Batch for Students in Student Group,Valider le Lot pour les Étudiants Validate Enrolled Course for Students in Student Group,Valider le Cours Inscrit pour les Étudiants en Groupe Étudiant, "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.","Pour un groupe étudiant basé sur un cours, le cours sera validé pour chaque élève inscrit aux cours du programme.", Make Academic Term Mandatory,Faire un terme académique obligatoire, -"If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.","Si cette option est activée, le champ Période académique sera obligatoire dans l'outil d'inscription au programme.", +"If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.","Si cette option est activée, le champ Période académique sera obligatoire dans l'outil d'inscription au programme.", Instructor Records to be created by,Les Enregistrements de l'Instructeur seront créés par, Employee Number,Numéro d'Employé, LMS Settings,Paramètres LMS, @@ -5760,7 +5760,7 @@ Guardian Interest,Part du Tuteur, Interest,Intérêt, Guardian Student,Tuteur de l'Étudiant, EDU-INS-.YYYY.-,EDU-INS-YYYY.-, -Instructor Log,Journal de l'instructeur, +Instructor Log,Journal de l'instructeur, Other details,Autres Détails, Option,Option, Is Correct,Est correct, @@ -5768,15 +5768,15 @@ Program Name,Nom du Programme, Program Abbreviation,Abréviation du Programme, Courses,Cours, Is Published,Est publié, -Allow Self Enroll,Autoriser l'auto-inscription, +Allow Self Enroll,Autoriser l'auto-inscription, Is Featured,Est en vedette, -Intro Video,Vidéo d'introduction, +Intro Video,Vidéo d'introduction, Program Course,Cours du Programme, School House,Maison de l'École, Boarding Student,Enregistrement Étudiant, Check this if the Student is residing at the Institute's Hostel.,Vérifiez si l'Étudiant réside à la Résidence de l'Institut., Walking,En Marchant, -Institute's Bus,Bus de l'Institut, +Institute's Bus,Bus de l'Institut, Public Transport,Transports Publics, Self-Driving Vehicle,Véhicule Autonome, Pick/Drop by Guardian,Déposé/Récupéré par le Tuteur, @@ -5787,7 +5787,7 @@ Program Enrollment Tool,Outil d’Inscription au Programme, Get Students From,Obtenir les Étudiants De, Student Applicant,Candidature Étudiante, Get Students,Obtenir les Étudiants, -Enrollment Details,Détails d'inscription, +Enrollment Details,Détails d'inscription, New Program,Nouveau Programme, New Student Batch,Nouveau groupe d'étudiants, Enroll Students,Inscrire des Étudiants, @@ -5846,7 +5846,7 @@ Admission Start Date,Date de Début de l'Admission, Admission End Date,Date de Fin de l'Admission, Publish on website,Publier sur le site web, Eligibility and Details,Admissibilité et Détails, -Student Admission Program,Programme d'admission des étudiants, +Student Admission Program,Programme d'admission des étudiants, Minimum Age,Âge Minimum, Maximum Age,Âge Maximum, Application Fee,Frais de Dossier, @@ -5883,14 +5883,14 @@ Will show the student as Present in Student Monthly Attendance Report,Affichera Student Log,Journal des Étudiants, Academic,Académique, Achievement,Réalisation, -Student Report Generation Tool,Outil de génération de rapports d'étudiants, -Include All Assessment Group,Inclure tout le groupe d'évaluation, +Student Report Generation Tool,Outil de génération de rapports d'étudiants, +Include All Assessment Group,Inclure tout le groupe d'évaluation, Show Marks,Afficher les notes, Add letterhead,Ajouter un en-tête, Print Section,Section d'impression, Total Parents Teacher Meeting,Total des réunions parents/professeur, Attended by Parents,Les parents ont participé, -Assessment Terms,Conditions d'évaluation, +Assessment Terms,Conditions d'évaluation, Student Sibling,Frère et Sœur de l'Étudiant, Studying in Same Institute,Étudier au même Institut, Student Siblings,Frères et Sœurs de l'Étudiants, @@ -5898,10 +5898,10 @@ Topic Content,Contenu du sujet, Amazon MWS Settings,Paramètres Amazon MWS, ERPNext Integrations,Intégrations ERPNext, Enable Amazon,Activer Amazon, -MWS Credentials,Informations d'identification MWS, +MWS Credentials,Informations d'identification MWS, Seller ID,ID du vendeur, -AWS Access Key ID,ID de clé d'accès AWS, -MWS Auth Token,Jeton d'authentification MWS, +AWS Access Key ID,ID de clé d'accès AWS, +MWS Auth Token,Jeton d'authentification MWS, Market Place ID,Identifiant de la place du marché, AU,AU, BR,BR, @@ -5919,12 +5919,12 @@ Market Place Account Group,Groupe de comptes de marché, After Date,Après la date, Amazon will synch data updated after this date,Amazon synchronisera les données mises à jour après cette date, Get financial breakup of Taxes and charges data by Amazon ,Obtenez la répartition financière des taxes et des données de facturation par Amazon, -Click this button to pull your Sales Order data from Amazon MWS.,Cliquez sur ce bouton pour extraire vos données de commande client d'Amazon MWS., +Click this button to pull your Sales Order data from Amazon MWS.,Cliquez sur ce bouton pour extraire vos données de commande client d'Amazon MWS., Check this to enable a scheduled Daily synchronization routine via scheduler,Cochez cette case pour activer une routine de synchronisation quotidienne programmée via le planificateur, Max Retry Limit,Max Retry Limit, Exotel Settings,Paramètres Exotel, Account SID,Compte SID, -API Token,Jeton d'API, +API Token,Jeton d'API, GoCardless Mandate,Mandat GoCardless, Mandate,Mandat, GoCardless Customer,Client GoCardless, @@ -5939,15 +5939,15 @@ Plaid Environment,Environnement écossais, sandbox,bac à sable, development,développement, QuickBooks Migrator,QuickBooks Migrator, -Application Settings,Paramètres de l'application, +Application Settings,Paramètres de l'application, Token Endpoint,Point de terminaison de jeton, Scope,Portée, -Authorization Settings,Paramètres d'autorisation, +Authorization Settings,Paramètres d'autorisation, Authorization Endpoint,Autorisation Endpoint, -Authorization URL,URL d'autorisation, +Authorization URL,URL d'autorisation, Quickbooks Company ID,ID Quickbooks de la société, Company Settings,des paramètres de l'entreprise, -Default Shipping Account,Compte d'expédition par défaut, +Default Shipping Account,Compte d'expédition par défaut, Default Warehouse,Entrepôt par Défaut, Default Cost Center,Centre de Coûts par Défaut, Undeposited Funds Account,Compte de fonds non déposés, @@ -5956,7 +5956,7 @@ Request Data,Données de la requête, Shopify Settings,Paramètres de Shopify, status html,Statut, Enable Shopify,Activer Shopify, -App Type,Type d'application, +App Type,Type d'application, Last Sync Datetime,Dernière date de synchronisation, Shop URL,URL de la boutique, eg: frappe.myshopify.com,par exemple: frappe.myshopify.com, @@ -6004,15 +6004,15 @@ Secret,Secret, API consumer key,Clé de consommateur API, API consumer secret,Secret de consommateur API, Tax Account,Compte de taxes, -Freight and Forwarding Account,Compte de fret et d'expédition, +Freight and Forwarding Account,Compte de fret et d'expédition, Creation User,Création utilisateur, "The user that will be used to create Customers, Items and Sales Orders. This user should have the relevant permissions.","Utilisateur qui sera utilisé pour créer des clients, des articles et des commandes clients. Cet utilisateur doit avoir les autorisations appropriées.", -"This warehouse will be used to create Sales Orders. The fallback warehouse is ""Stores"".",Cet entrepôt sera utilisé pour créer des commandes client. L'entrepôt de secours est "Stores"., +"This warehouse will be used to create Sales Orders. The fallback warehouse is ""Stores"".",Cet entrepôt sera utilisé pour créer des commandes client. L'entrepôt de secours est "Stores"., "The fallback series is ""SO-WOO-"".",La série de repli est "SO-WOO-"., This company will be used to create Sales Orders.,Cette société sera utilisée pour créer des commandes client., Delivery After (Days),Livraison après (jours), -This is the default offset (days) for the Delivery Date in Sales Orders. The fallback offset is 7 days from the order placement date.,Il s'agit du décalage par défaut (jours) pour la date de livraison dans les commandes client. La compensation de repli est de 7 jours à compter de la date de passation de la commande., -"This is the default UOM used for items and Sales orders. The fallback UOM is ""Nos"".",Il s'agit de l'UOM par défaut utilisée pour les articles et les commandes clients. La MOU de repli est "Nos"., +This is the default offset (days) for the Delivery Date in Sales Orders. The fallback offset is 7 days from the order placement date.,Il s'agit du décalage par défaut (jours) pour la date de livraison dans les commandes client. La compensation de repli est de 7 jours à compter de la date de passation de la commande., +"This is the default UOM used for items and Sales orders. The fallback UOM is ""Nos"".",Il s'agit de l'UOM par défaut utilisée pour les articles et les commandes clients. La MOU de repli est "Nos"., Endpoints,Points de terminaison, Endpoint,Point de terminaison, Antibiotic Name,Nom de l'Antibiotique, @@ -6056,14 +6056,14 @@ Charges,Charges, Default Currency,Devise par Défaut, Healthcare Schedule Time Slot,Horaire horaire, Parent Service Unit,Service parent, -Service Unit Type,Type d'unité de service, +Service Unit Type,Type d'unité de service, Allow Appointments,Autoriser les rendez-vous, Allow Overlap,Autoriser le chevauchement, Inpatient Occupancy,Occupation des patients hospitalisés, -Occupancy Status,Statut d'occupation, +Occupancy Status,Statut d'occupation, Vacant,Vacant, Occupied,Occupé, -Item Details,Détails d'article, +Item Details,Détails d'article, UOM Conversion in Hours,Conversion UOM en heures, Rate / UOM,Taux / UM, Change in Item,Modification dans l'Article, @@ -6089,7 +6089,7 @@ Reminder Message,Message de Rappel, Remind Before,Rappeler Avant, Laboratory Settings,Paramètres de laboratoire, Employee name and designation in print,Nom et désignation de l'employé sur l'imprimé, -Custom Signature in Print,Signature personnalisée dans l'impression, +Custom Signature in Print,Signature personnalisée dans l'impression, Laboratory SMS Alerts,Alertes SMS de laboratoire, Check In,Arrivée, Check Out,Départ, @@ -6146,7 +6146,7 @@ Require Result Value,Nécessite la Valeur du Résultat, Normal Test Template,Modèle de Test Normal, Patient Demographics,Démographie du Patient, HLC-PAT-.YYYY.-,HLC-PAT-. AAAA.-, -Inpatient Status,Statut d'hospitalisation, +Inpatient Status,Statut d'hospitalisation, Personal and Social History,Antécédents Personnels et Sociaux, Marital Status,État Civil, Married,Marié, @@ -6164,7 +6164,7 @@ Other Risk Factors,Autres facteurs de risque, Patient Details,Détails du patient, Additional information regarding the patient,Informations complémentaires concernant le patient, Patient Age,Âge du patient, -More Info,Plus d'infos, +More Info,Plus d'infos, Referring Practitioner,Praticien référant, Reminded,Rappelé, Parameters,Paramètres, @@ -6181,7 +6181,7 @@ Spouse,Époux, Family,Famille, Schedule Name,Nom du calendrier, Time Slots,Créneaux Horaires, -Practitioner Service Unit Schedule,Horaire de l'unité de service du praticien, +Practitioner Service Unit Schedule,Horaire de l'unité de service du praticien, Procedure Name,Nom de la procédure, Appointment Booked,Rendez-vous pris, Procedure Created,Procédure créée, @@ -6217,25 +6217,25 @@ One Sided,Une face, Blood Pressure (systolic),Pression Artérielle (Systolique), Blood Pressure (diastolic),Pression Artérielle (Diastolique), Blood Pressure,Pression Artérielle, -"Normal resting blood pressure in an adult is approximately 120 mmHg systolic, and 80 mmHg diastolic, abbreviated ""120/80 mmHg""","La tension artérielle normale chez un adulte est d'environ 120 mmHg systolique et 80 mmHg diastolique, abrégé "120/80 mmHg"", +"Normal resting blood pressure in an adult is approximately 120 mmHg systolic, and 80 mmHg diastolic, abbreviated ""120/80 mmHg""","La tension artérielle normale chez un adulte est d'environ 120 mmHg systolique et 80 mmHg diastolique, abrégé "120/80 mmHg"", Nutrition Values,Valeurs Nutritionnelles, Height (In Meter),Hauteur (en Mètres), Weight (In Kilogram),Poids (En Kilogramme), BMI,IMC, -Hotel Room,Chambre d'hôtel, -Hotel Room Type,Type de chambre d'hôtel, +Hotel Room,Chambre d'hôtel, +Hotel Room Type,Type de chambre d'hôtel, Capacity,Capacité, Extra Bed Capacity,Capacité de lits supplémentaire, -Hotel Manager,Directeur de l'hôtel, +Hotel Manager,Directeur de l'hôtel, Hotel Room Amenity,Équipement de la chambre d'hôtel, Billable,Facturable, Hotel Room Package,Forfait de la chambre d'hôtel, Amenities,Équipements, -Hotel Room Pricing,Prix de la chambre d'hôtel, +Hotel Room Pricing,Prix de la chambre d'hôtel, Hotel Room Pricing Item,Article de prix de la chambre d'hôtel, Hotel Room Pricing Package,Forfait de prix de la chambre d'hôtel, Hotel Room Reservation,Réservation de la chambre d'hôtel, -Guest Name,Nom de l'invité, +Guest Name,Nom de l'invité, Late Checkin,Arrivée tardive, Booked,Réservé, Hotel Reservation User,Utilisateur chargé des réservations d'hôtel, @@ -6264,7 +6264,7 @@ Closing Notes,Notes de clôture, Appointment Letter content,Contenu de la lettre de nomination, Appraisal,Estimation, HR-APR-.YY.-.MM.,HR-APR-.YY.-.MM., -Appraisal Template,Modèle d'évaluation, +Appraisal Template,Modèle d'évaluation, For Employee Name,Nom de l'Employé, Goals,Objectifs, Calculate Total Score,Calculer le Résultat Total, @@ -6322,7 +6322,7 @@ Applicable for external driver,Applicable pour pilote externe, Cellphone Number,Numéro de téléphone portable, License Details,Détails de la licence, License Number,Numéro de licence, -Issuing Date,Date d'émission, +Issuing Date,Date d'émission, Driving License Categories,Catégories de permis de conduire, Driving License Category,Catégorie de permis de conduire, Fleet Manager,Gestionnaire de Flotte, @@ -6330,7 +6330,7 @@ Driver licence class,Classe de permis de conduire, HR-EMP-,HR-EMP-, Employment Type,Type d'Emploi, Emergency Contact,Contact en cas d'Urgence, -Emergency Contact Name,Nom à contacter en cas d'urgence, +Emergency Contact Name,Nom à contacter en cas d'urgence, Emergency Phone,Téléphone d'Urgence, ERPNext User,Utilisateur ERPNext, "System User (login) ID. If set, it will become default for all HR forms.","L'ID (de connexion) de l'Utilisateur Système. S'il est défini, il deviendra la valeur par défaut pour tous les formulaires des RH.", @@ -6346,7 +6346,7 @@ Department and Grade,Département et échelon, Reports to,Rapports À, Attendance and Leave Details,Détails de présence et de congés, Leave Policy,Politique de congé, -Attendance Device ID (Biometric/RF tag ID),Périphérique d'assistance (identifiant d'étiquette biométrique / RF), +Attendance Device ID (Biometric/RF tag ID),Périphérique d'assistance (identifiant d'étiquette biométrique / RF), Applicable Holiday List,Liste de Vacances Valable, Default Shift,Décalage par défaut, Salary Details,Détails du salaire, @@ -6397,7 +6397,7 @@ HR-EAD-.YYYY.-,HR-EAD-.YYYY.-, Due Advance Amount,Montant de l'avance dû, Returned Amount,Montant retourné, Claimed,Réclamé, -Advance Account,Compte d'avances, +Advance Account,Compte d'avances, Employee Attendance Tool,Outil de Gestion des Présences des Employés, Unmarked Attendance,Participation Non Marquée, Employees HTML,Employés HTML, @@ -6409,7 +6409,7 @@ Remaining Benefits (Yearly),Prestations sociales restantes (par année), Payroll Period,Période de paie, Benefits Applied,Prestations demandées, Dispensed Amount (Pro-rated),Montant distribué (au prorata), -Employee Benefit Application Detail,Détail de la demande d'avantages sociaux, +Employee Benefit Application Detail,Détail de la demande d'avantages sociaux, Earning Component,Composante de revenu, Pay Against Benefit Claim,Payer la demande de prestations, Max Benefit Amount,Montant maximal des prestations, @@ -6420,15 +6420,15 @@ Claim Benefit For,Demande de prestations pour, Max Amount Eligible,Montant maximum admissible, Expense Proof,Preuves de dépenses, Employee Boarding Activity,Activité d'intégration des nouveaux employés, -Activity Name,Nom de l'activité, +Activity Name,Nom de l'activité, Task Weight,Poids de la Tâche, -Required for Employee Creation,Obligatoire pour la création d'un employé, +Required for Employee Creation,Obligatoire pour la création d'un employé, Applicable in the case of Employee Onboarding,Applicable dans le cas de l'accueil des nouveaux employés, Employee Checkin,Enregistrement des employés, Log Type,Type de journal, OUT,EN DEHORS, Location / Device ID,Emplacement / ID de périphérique, -Skip Auto Attendance,Ignorer l'assistance automatique, +Skip Auto Attendance,Ignorer l'assistance automatique, Shift Start,Début de quart, Shift End,Fin de quart, Shift Actual Start,Décalage début effectif, @@ -6445,7 +6445,7 @@ Employee External Work History,Antécédents Professionnels de l'Employé, Total Experience,Expérience Totale, Default Leave Policy,Politique de congés par défaut, Default Salary Structure,Structure salariale par défaut, -Employee Group Table,Table de groupe d'employés, +Employee Group Table,Table de groupe d'employés, ERPNext User ID,ID utilisateur ERPNext, Employee Health Insurance,Assurance maladie des employés, Health Insurance Name,Nom de l'assurance santé, @@ -6465,34 +6465,34 @@ Employee Property History,Historique des propriétés des champs de la fiche emp Employee Separation,Départ des employés, Employee Separation Template,Modèle de départ des employés, Exit Interview Summary,Récapitulatif de l'entretien de sortie, -Employee Skill,Compétence de l'employé, +Employee Skill,Compétence de l'employé, Proficiency,Compétence, -Evaluation Date,Date d'évaluation, +Evaluation Date,Date d'évaluation, Employee Skill Map,Carte de compétences des employés, Employee Skills,Compétences des employés, Trainings,Des formations, Employee Tax Exemption Category,Catégorie d'exemption de taxe des employés, -Max Exemption Amount,Montant maximum d'exemption, +Max Exemption Amount,Montant maximum d'exemption, Employee Tax Exemption Declaration,Déclaration d'exemption de taxe, Declarations,Déclarations, Total Declared Amount,Montant total déclaré, Total Exemption Amount,Montant total de l'exonération, Employee Tax Exemption Declaration Category,Catégorie de déclaration d'exemption de taxe, -Exemption Sub Category,Sous-catégorie d'exemption, -Exemption Category,Catégorie d'exemption, +Exemption Sub Category,Sous-catégorie d'exemption, +Exemption Category,Catégorie d'exemption, Maximum Exempted Amount,Montant maximum exonéré, Declared Amount,Montant Déclaré, Employee Tax Exemption Proof Submission,Soumission d'une preuve d'exemption de taxe, Submission Date,Date de soumission, -Tax Exemption Proofs,Preuves d'exonération fiscale, +Tax Exemption Proofs,Preuves d'exonération fiscale, Total Actual Amount,Montant total total, Employee Tax Exemption Proof Submission Detail,Détails de la soumission de preuve d'exemption de taxe, -Maximum Exemption Amount,Montant maximum d'exemption, +Maximum Exemption Amount,Montant maximum d'exemption, Type of Proof,Type de preuve, Actual Amount,Montant actuel, Employee Tax Exemption Sub Category,Sous-catégorie d'exemption de taxe, Tax Exemption Category,Catégorie d'exonération fiscale, -Employee Training,Entrainement d'employé, +Employee Training,Entrainement d'employé, Training Date,Date de formation, Employee Transfer,Transfert des employés, Transfer Date,Date de transfert, @@ -6505,7 +6505,7 @@ Employee Transfer Property,Propriété des champs pour le transfert des employé HR-EXP-.YYYY.-,HR-EXP-. AAAA.-, Expense Taxes and Charges,Frais et taxes, Total Sanctioned Amount,Montant Total Validé, -Total Advance Amount,Montant total de l'avance, +Total Advance Amount,Montant total de l'avance, Total Claimed Amount,Montant Total Réclamé, Total Amount Reimbursed,Montant Total Remboursé, Vehicle Log,Journal du Véhicule, @@ -6540,7 +6540,7 @@ Include holidays in Total no. of Working Days,Inclure les vacances dans le nombr Email Salary Slip to Employee,Envoyer la Fiche de Paie à l'Employé par Mail, Emails salary slip to employee based on preferred email selected in Employee,Envoi des fiches de paie à l'employé par Email en fonction de l'email sélectionné dans la fiche Employé, Encrypt Salary Slips in Emails,Crypter les bulletins de salaire dans les courriels, -"The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.",La fiche de salaire envoyée à l'employé par courrier électronique sera protégée par un mot de passe. Le mot de passe sera généré en fonction de la politique de mot de passe., +"The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.",La fiche de salaire envoyée à l'employé par courrier électronique sera protégée par un mot de passe. Le mot de passe sera généré en fonction de la politique de mot de passe., Password Policy,Politique de mot de passe, Example: SAL-{first_name}-{date_of_birth.year}
    This will generate a password like SAL-Jane-1972,Exemple: SAL- {prenom} - {date_naissance.année}
    Cela générera un mot de passe comme SAL-Jane-1972, Leave Settings,Paramètres des Congés, @@ -6551,10 +6551,10 @@ Leave Approver Mandatory In Leave Application,Approbateur de congés obligatoire Show Leaves Of All Department Members In Calendar,Afficher les congés de tous les membres du département dans le calendrier, Auto Leave Encashment,Auto Leave Encashment, Restrict Backdated Leave Application,Restreindre la demande de congé antidaté, -Hiring Settings,Paramètres d'embauche, -Check Vacancies On Job Offer Creation,Vérifier les offres d'emploi lors de la création d'une offre d'emploi, -Identification Document Type,Type de document d'identification, -Standard Tax Exemption Amount,Montant de l'exemption fiscale standard, +Hiring Settings,Paramètres d'embauche, +Check Vacancies On Job Offer Creation,Vérifier les offres d'emploi lors de la création d'une offre d'emploi, +Identification Document Type,Type de document d'identification, +Standard Tax Exemption Amount,Montant de l'exemption fiscale standard, Taxable Salary Slabs,Paliers de salaire imposables, Applicant for a Job,Candidat à un Emploi, Accepted,Accepté, @@ -6606,11 +6606,11 @@ Leave Block List Date,Date de la Liste de Blocage des Congés, Block Date,Bloquer la Date, Leave Control Panel,Quitter le Panneau de Configuration, Select Employees,Sélectionner les Employés, -Employment Type (optional),Type d'emploi (facultatif), +Employment Type (optional),Type d'emploi (facultatif), Branch (optional),Branche (optionnel), Department (optional),Département (optionnel), Designation (optional),Désignation (facultatif), -Employee Grade (optional),Grade d'employé (facultatif), +Employee Grade (optional),Grade d'employé (facultatif), Employee (optional),Employé (facultatif), Allocate Leaves,Allouer des feuilles, Carry Forward,Reporter, @@ -6642,8 +6642,8 @@ Maximum Carry Forwarded Leaves,Nombre maximal de congés reportés, Expire Carry Forwarded Leaves (Days),Expirer les congés reportés (jours), Calculated in days,Calculé en jours, Encashment,Encaissement, -Allow Encashment,Autoriser l'encaissement, -Encashment Threshold Days,Jours de seuil d'encaissement, +Allow Encashment,Autoriser l'encaissement, +Encashment Threshold Days,Jours de seuil d'encaissement, Earned Leave,Congés acquis, Is Earned Leave,Est un congé acquis, Earned Leave Frequency,Fréquence d'acquisition des congés, @@ -6653,7 +6653,7 @@ Payroll Frequency,Fréquence de la Paie, Fortnightly,Bimensuel, Bimonthly,Bimensuel, Employees,Employés, -Number Of Employees,Nombre d'employés, +Number Of Employees,Nombre d'employés, Employee Details,Détails des employés, Validate Attendance,Valider la présence, Salary Slip Based on Timesheet,Fiche de Paie basée sur la Feuille de Temps, @@ -6673,9 +6673,9 @@ Abbr,Abré, Depends on Payment Days,Dépend des jours de paiement, Is Tax Applicable,Est taxable, Variable Based On Taxable Salary,Variable basée sur le salaire imposable, -Round to the Nearest Integer,Arrondir à l'entier le plus proche, +Round to the Nearest Integer,Arrondir à l'entier le plus proche, Statistical Component,Composante Statistique, -"If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ","Si cette option est sélectionnée, la valeur spécifiée ou calculée dans ce composant ne contribuera pas aux gains ou aux déductions. Cependant, sa valeur peut être référencée par d'autres composants qui peuvent être ajoutés ou déduits.", +"If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ","Si cette option est sélectionnée, la valeur spécifiée ou calculée dans ce composant ne contribuera pas aux gains ou aux déductions. Cependant, sa valeur peut être référencée par d'autres composants qui peuvent être ajoutés ou déduits.", Flexible Benefits,Avantages sociaux variables, Is Flexible Benefit,Est un avantage flexible, Max Benefit Amount (Yearly),Montant maximum des prestations sociales (annuel), @@ -6703,7 +6703,7 @@ Earnings,Bénéfices, Deductions,Déductions, Employee Loan,Prêt Employé, Total Principal Amount,Montant total du capital, -Total Interest Amount,Montant total de l'intérêt, +Total Interest Amount,Montant total de l'intérêt, Total Loan Repayment,Total de Remboursement du Prêt, net pay info,Info de salaire net, Gross Pay - Total Deduction - Loan Repayment,Salaire Brut - Déductions Totales - Remboursement de Prêt, @@ -6719,17 +6719,17 @@ Shift Assignment,Affectation de quart, Shift Type,Type de quart, Shift Request,Demande de quart, Enable Auto Attendance,Activer la présence automatique, -Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.,Marquez la présence sur la base de 'Enregistrement des employés' pour les employés affectés à ce poste., +Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.,Marquez la présence sur la base de 'Enregistrement des employés' pour les employés affectés à ce poste., Auto Attendance Settings,Paramètres de présence automatique, Determine Check-in and Check-out,Déterminer les entrées et les sorties, Alternating entries as IN and OUT during the same shift,Alterner les entrées comme IN et OUT pendant le même quart, -Strictly based on Log Type in Employee Checkin,Strictement basé sur le type de journal dans l'enregistrement des employés, +Strictly based on Log Type in Employee Checkin,Strictement basé sur le type de journal dans l'enregistrement des employés, Working Hours Calculation Based On,Calcul des heures de travail basé sur, First Check-in and Last Check-out,Premier enregistrement et dernier départ, Every Valid Check-in and Check-out,Chaque enregistrement valide et check-out, -Begin check-in before shift start time (in minutes),Commencez l'enregistrement avant l'heure de début du poste (en minutes), -The time before the shift start time during which Employee Check-in is considered for attendance.,Heure avant l'heure de début du quart pendant laquelle l'enregistrement des employés est pris en compte pour la présence., -Allow check-out after shift end time (in minutes),Autoriser le départ après l'heure de fin du quart (en minutes), +Begin check-in before shift start time (in minutes),Commencez l'enregistrement avant l'heure de début du poste (en minutes), +The time before the shift start time during which Employee Check-in is considered for attendance.,Heure avant l'heure de début du quart pendant laquelle l'enregistrement des employés est pris en compte pour la présence., +Allow check-out after shift end time (in minutes),Autoriser le départ après l'heure de fin du quart (en minutes), Time after the end of shift during which check-out is considered for attendance.,Heure après la fin du quart de travail au cours de laquelle la prise en charge est prise en compte., Working Hours Threshold for Half Day,Seuil des heures de travail pour une demi-journée, Working hours below which Half Day is marked. (Zero to disable),Heures de travail en dessous desquelles la demi-journée est marquée. (Zéro à désactiver), @@ -6738,14 +6738,14 @@ Working hours below which Absent is marked. (Zero to disable),Heures de travail Process Attendance After,Processus de présence après, Attendance will be marked automatically only after this date.,La participation sera automatiquement marquée après cette date., Last Sync of Checkin,Dernière synchronisation de Checkin, -Last Known Successful Sync of Employee Checkin. Reset this only if you are sure that all Logs are synced from all the locations. Please don't modify this if you are unsure.,Dernière synchronisation réussie de l'enregistrement des employés. Réinitialisez cette opération uniquement si vous êtes certain que tous les journaux sont synchronisés à partir de tous les emplacements. S'il vous plaît ne modifiez pas cela si vous n'êtes pas sûr., -Grace Period Settings For Auto Attendance,Paramètres de période de grâce pour l'assistance automatique, -Enable Entry Grace Period,Activer la période de grâce d'entrée, +Last Known Successful Sync of Employee Checkin. Reset this only if you are sure that all Logs are synced from all the locations. Please don't modify this if you are unsure.,Dernière synchronisation réussie de l'enregistrement des employés. Réinitialisez cette opération uniquement si vous êtes certain que tous les journaux sont synchronisés à partir de tous les emplacements. S'il vous plaît ne modifiez pas cela si vous n'êtes pas sûr., +Grace Period Settings For Auto Attendance,Paramètres de période de grâce pour l'assistance automatique, +Enable Entry Grace Period,Activer la période de grâce d'entrée, Late Entry Grace Period,Délai de grâce pour entrée tardive, -The time after the shift start time when check-in is considered as late (in minutes).,L'heure après l'heure de début du quart de travail où l'enregistrement est considéré comme tardif (en minutes)., +The time after the shift start time when check-in is considered as late (in minutes).,L'heure après l'heure de début du quart de travail où l'enregistrement est considéré comme tardif (en minutes)., Enable Exit Grace Period,Activer la période de grâce de sortie, Early Exit Grace Period,Période de grâce de sortie anticipée, -The time before the shift end time when check-out is considered as early (in minutes).,L'heure avant l'heure de fin du quart de travail au moment du départ est considérée comme précoce (en minutes)., +The time before the shift end time when check-out is considered as early (in minutes).,L'heure avant l'heure de fin du quart de travail au moment du départ est considérée comme précoce (en minutes)., Skill Name,Nom de la compétence, Staffing Plan Details,Détails du plan de dotation, Staffing Plan Detail,Détail du plan de dotation, @@ -6797,8 +6797,8 @@ Travel Advance Required,Avance de déplacement requise, Departure Datetime,Date/Heure de départ, Arrival Datetime,Date/Heure d'arrivée, Lodging Required,Hébergement requis, -Preferred Area for Lodging,Zone préférée pour l'hébergement, -Check-in Date,Date d'arrivée, +Preferred Area for Lodging,Zone préférée pour l'hébergement, +Check-in Date,Date d'arrivée, Check-out Date,Date de départ, Travel Request,Demande de déplacement, Travel Type,Type de déplacement, @@ -6808,15 +6808,15 @@ Travel Funding,Financement du déplacement, Require Full Funding,Nécessite un financement complet, Fully Sponsored,Entièrement commandité, "Partially Sponsored, Require Partial Funding","Partiellement sponsorisé, nécessite un financement partiel", -Copy of Invitation/Announcement,Copie de l'invitation / annonce, +Copy of Invitation/Announcement,Copie de l'invitation / annonce, "Details of Sponsor (Name, Location)","Détails du commanditaire (nom, lieu)", Identification Document Number,Numéro du document d'identification, Any other details,Tout autre détail, Costing Details,Détails des coûts, Costing,Coût, -Event Details,Détails de l'évènement, -Name of Organizer,Nom de l'organisateur, -Address of Organizer,Adresse de l'organisateur, +Event Details,Détails de l'évènement, +Name of Organizer,Nom de l'organisateur, +Address of Organizer,Adresse de l'organisateur, Travel Request Costing,Coût de la demande de déplacement, Expense Type,Type de dépense, Sponsored Amount,Montant sponsorisé, @@ -6865,14 +6865,14 @@ Inspection,Inspection, Mileage,Kilométrage, Hub Tracked Item,Article suivi sur le Hub, Hub Node,Noeud du Hub, -Image List,Liste d'images, +Image List,Liste d'images, Item Manager,Gestionnaire d'Article, Hub User,Utilisateur du hub, Hub Password,Mot de passe Hub, Hub Users,Utilisateurs du Hub, Marketplace Settings,Paramètres du marché, Disable Marketplace,Désactiver le marché, -Marketplace URL (to hide and update label),URL du marché (pour masquer et mettre à jour l'étiquette), +Marketplace URL (to hide and update label),URL du marché (pour masquer et mettre à jour l'étiquette), Registered,Inscrit, Sync in Progress,Synchronisation en cours, Hub Seller Name,Nom du vendeur, @@ -6946,8 +6946,8 @@ Unpledge Time,Désengager le temps, Unpledge Type,Type de désengagement, Loan Name,Nom du Prêt, Rate of Interest (%) Yearly,Taux d'Intérêt (%) Annuel, -Penalty Interest Rate (%) Per Day,Taux d'intérêt de pénalité (%) par jour, -Penalty Interest Rate is levied on the pending interest amount on a daily basis in case of delayed repayment ,Le taux d'intérêt de pénalité est prélevé quotidiennement sur le montant des intérêts en attente en cas de retard de remboursement, +Penalty Interest Rate (%) Per Day,Taux d'intérêt de pénalité (%) par jour, +Penalty Interest Rate is levied on the pending interest amount on a daily basis in case of delayed repayment ,Le taux d'intérêt de pénalité est prélevé quotidiennement sur le montant des intérêts en attente en cas de retard de remboursement, Grace Period in Days,Délai de grâce en jours, Pledge,Gage, Post Haircut Amount,Montant de la coupe de cheveux, @@ -6961,7 +6961,7 @@ Loan Repayment Entry,Entrée de remboursement de prêt, Sanctioned Loan Amount,Montant du prêt sanctionné, Sanctioned Amount Limit,Limite de montant sanctionnée, Unpledge,Désengager, -Against Pledge,Contre l'engagement, +Against Pledge,Contre l'engagement, Haircut,la Coupe de cheveux, MAT-MSH-.YYYY.-,MAT-MSH-YYYY.-, Generate Schedule,Créer un Échéancier, @@ -7002,7 +7002,7 @@ Transfer Material Against,Transférer du matériel contre, Routing,Routage, Materials,Matériels, Quality Inspection Required,Inspection de qualité requise, -Quality Inspection Template,Modèle d'inspection de la qualité, +Quality Inspection Template,Modèle d'inspection de la qualité, Scrap,Mettre au Rebut, Scrap Items,Mettre au Rebut des Articles, Operating Cost,Coût d'Exploitation, @@ -7012,7 +7012,7 @@ Operating Cost (Company Currency),Coût d'Exploitation (Devise Société), Raw Material Cost (Company Currency),Coût de la matière première (devise de la société), Scrap Material Cost(Company Currency),Coût de Mise au Rebut des Matériaux (Devise Société), Total Cost,Coût Total, -Total Cost (Company Currency),Coût total (devise de l'entreprise), +Total Cost (Company Currency),Coût total (devise de l'entreprise), Materials Required (Exploded),Matériel Requis (Éclaté), Exploded Items,Articles éclatés, Item Image (if not slideshow),Image de l'Article (si ce n'est diaporama), @@ -7023,9 +7023,9 @@ Show Operations,Afficher Opérations, Website Description,Description du Site Web, BOM Explosion Item,Article Eclaté LDM, Qty Consumed Per Unit,Qté Consommée Par Unité, -Include Item In Manufacturing,Inclure l'article dans la fabrication, +Include Item In Manufacturing,Inclure l'article dans la fabrication, BOM Item,Article LDM, -Item operation,Opération de l'article, +Item operation,Opération de l'article, Rate & Amount,Taux et Montant, Basic Rate (Company Currency),Taux de Base (Devise de la Société ), Scrap %,% de Rebut, @@ -7118,24 +7118,24 @@ Product Bundle Item,Article d'un Ensemble de Produits, Production Plan Material Request,Demande de Matériel du Plan de Production, Production Plan Sales Order,Commande Client du Plan de Production, Sales Order Date,Date de la Commande Client, -Routing Name,Nom d'acheminement, +Routing Name,Nom d'acheminement, MFG-WO-.YYYY.-,MFG-WO-.YYYY.-, Item To Manufacture,Article à produire, Material Transferred for Manufacturing,Matériel Transféré pour la Production, Manufactured Qty,Qté Produite, Use Multi-Level BOM,Utiliser LDM à Plusieurs Niveaux, Plan material for sub-assemblies,Plan de matériaux pour les sous-ensembles, -Skip Material Transfer to WIP Warehouse,Ignorer le transfert de matériel vers l'entrepôt WIP, +Skip Material Transfer to WIP Warehouse,Ignorer le transfert de matériel vers l'entrepôt WIP, Check if material transfer entry is not required,Vérifiez si une un transfert de matériel n'est pas requis, -Backflush Raw Materials From Work-in-Progress Warehouse,Rembourrage des matières premières dans l'entrepôt de travaux en cours, +Backflush Raw Materials From Work-in-Progress Warehouse,Rembourrage des matières premières dans l'entrepôt de travaux en cours, Update Consumed Material Cost In Project,Mettre à jour le coût des matières consommées dans le projet, Warehouses,Entrepôts, -This is a location where raw materials are available.,C'est un endroit où les matières premières sont disponibles., +This is a location where raw materials are available.,C'est un endroit où les matières premières sont disponibles., Work-in-Progress Warehouse,Entrepôt des Travaux en Cours, -This is a location where operations are executed.,Il s'agit d'un emplacement où les opérations sont exécutées., -This is a location where final product stored.,Il s'agit d'un emplacement où le produit final est stocké., +This is a location where operations are executed.,Il s'agit d'un emplacement où les opérations sont exécutées., +This is a location where final product stored.,Il s'agit d'un emplacement où le produit final est stocké., Scrap Warehouse,Entrepôt de Rebut, -This is a location where scraped materials are stored.,Il s'agit d'un emplacement où les matériaux raclés sont stockés., +This is a location where scraped materials are stored.,Il s'agit d'un emplacement où les matériaux raclés sont stockés., Required Items,Articles Requis, Actual Start Date,Date de Début Réelle, Planned End Date,Date de Fin Prévue, @@ -7205,13 +7205,13 @@ Grant Description,Description de la subvention, Requested Amount,Quantité exigée, Has any past Grant Record,A obtenu des bourses par le passé, Show on Website,Afficher sur le site Web, -Assessment Mark (Out of 10),Note d'évaluation (sur 10), -Assessment Manager,Gestionnaire d'évaluation, +Assessment Mark (Out of 10),Note d'évaluation (sur 10), +Assessment Manager,Gestionnaire d'évaluation, Email Notification Sent,Notification par e-mail envoyée, NPO-MEM-.YYYY.-,NPO-MEM-YYYY.-, -Membership Expiry Date,Date d'expiration de l'adhésion, +Membership Expiry Date,Date d'expiration de l'adhésion, Non Profit Member,Membre de l'association, -Membership Status,Statut d'adhésion, +Membership Status,Statut d'adhésion, Member Since,Membre depuis, Volunteer Name,Nom du bénévole, Volunteer Type,Type de bénévole, @@ -7227,12 +7227,12 @@ Volunteer Skills,Compétences bénévoles, Volunteer Skill,Compétence bénévole, Homepage,Page d'Accueil, Hero Section Based On,Section de héros basée sur, -Homepage Section,Section de la page d'accueil, +Homepage Section,Section de la page d'accueil, Hero Section,Section de héros, Tag Line,Ligne de Tag, Company Tagline for website homepage,Slogan de la Société pour la page d'accueil du site web, Company Description for website homepage,Description de la Société pour la page d'accueil du site web, -Homepage Slideshow,Diaporama de la page d'accueil, +Homepage Slideshow,Diaporama de la page d'accueil, "URL for ""All Products""","URL pour ""Tous les Produits""", Products to be shown on website homepage,Produits destinés à être affichés sur la page d’accueil du site web, Homepage Featured Product,Produit Présenté sur la Page d'Accueil, @@ -7249,12 +7249,12 @@ Subtitle,Sous-titre, Products Settings,Paramètres des Produits, Home Page is Products,La Page d'Accueil est Produits, "If checked, the Home page will be the default Item Group for the website","Si cochée, la page d'Accueil pour le site sera le Groupe d'Article par défaut", -Show Availability Status,Afficher l'état de la disponibilité, +Show Availability Status,Afficher l'état de la disponibilité, Product Page,Page produit, Products per Page,Produits par page, Enable Field Filters,Activer les filtres de champ, -Item Fields,Champs de l'article, -Enable Attribute Filters,Activer les filtres d'attributs, +Item Fields,Champs de l'article, +Enable Attribute Filters,Activer les filtres d'attributs, Attributes,Attributs, Hide Variants,Masquer les variantes, Website Attribute,Attribut de site Web, @@ -7391,7 +7391,7 @@ October,octobre, November,novembre, December,décembre, JSON Output,Sortie JSON, -Invoices with no Place Of Supply,Factures sans lieu d'approvisionnement, +Invoices with no Place Of Supply,Factures sans lieu d'approvisionnement, Import Supplier Invoice,Importer la facture fournisseur, Invoice Series,Série de factures, Upload XML Invoices,Télécharger des factures XML, @@ -7424,7 +7424,7 @@ SAL-CAM-.YYYY.-,SAL-CAM-YYYY.-, Campaign Schedules,Horaires de campagne, Buyer of Goods and Services.,Acheteur des Biens et Services., CUST-.YYYY.-,CUST-.YYYY.-, -Default Company Bank Account,Compte bancaire d'entreprise par défaut, +Default Company Bank Account,Compte bancaire d'entreprise par défaut, From Lead,Du Prospect, Account Manager,Gestionnaire de compte, Default Price List,Liste des Prix par Défaut, @@ -7466,7 +7466,7 @@ POS Closing Voucher Details,Détail du bon de clôture du PDV, Collected Amount,Montant collecté, Expected Amount,Montant prévu, POS Closing Voucher Invoices,Factures du bon de clôture du PDV, -Quantity of Items,Quantité d'articles, +Quantity of Items,Quantité d'articles, POS Closing Voucher Taxes,Taxes du bon de clotûre du PDV, "Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" as ""Yes"".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials","Regroupement d' **Articles** dans un autre **Article**. Ceci est utile si vous regroupez certains **Articles** dans un lot et que vous maintenez l'inventaire des **Articles** du lot et non de l'**Article** composé. L'**Article** composé aura ""Article En Stock"" à ""Non"" et ""Article À Vendre"" à ""Oui"". Exemple : Si vous vendez des Ordinateurs Portables et Sacs à Dos séparément et qu'il y a un prix spécial si le client achète les deux, alors l'Ordinateur Portable + le Sac à Dos sera un nouveau Produit Groupé. Remarque: LDM = Liste\nDes Matériaux", Parent Item,Article Parent, @@ -7564,7 +7564,7 @@ Default Values,Valeurs Par Défaut, Default Holiday List,Liste de Vacances par Défaut, Standard Working Hours,Heures de travail standard, Default Selling Terms,Conditions de vente par défaut, -Default Buying Terms,Conditions d'achat par défaut, +Default Buying Terms,Conditions d'achat par défaut, Default warehouse for Sales Return,Magasin par défaut pour retour de vente, Create Chart Of Accounts Based On,Créer un Plan Comptable Basé Sur, Standard Template,Modèle Standard, @@ -7579,8 +7579,8 @@ Total Monthly Sales,Total des Ventes Mensuelles, Default Cash Account,Compte de Caisse par Défaut, Default Receivable Account,Compte Client par Défaut, Round Off Cost Center,Centre de Coûts d’Arrondi, -Discount Allowed Account,Compte d'escompte autorisé, -Discount Received Account,Compte d'escompte reçu, +Discount Allowed Account,Compte d'escompte autorisé, +Discount Received Account,Compte d'escompte reçu, Exchange Gain / Loss Account,Compte de Profits / Pertes sur Change, Unrealized Exchange Gain/Loss Account,Compte de gains / pertes de change non réalisés, Allow Account Creation Against Child Company,Autoriser la création de compte contre une entreprise enfant, @@ -7608,7 +7608,7 @@ Company Logo,Logo de la société, Date of Incorporation,Date de constitution, Date of Commencement,Date de démarrage, Phone No,N° de Téléphone, -Company Description,Description de l'entreprise, +Company Description,Description de l'entreprise, Registration Details,Informations Légales, Company registration numbers for your reference. Tax numbers etc.,"Numéro d'immatriculation de la Société pour votre référence. Numéros de taxes, etc.", Delete Company Transactions,Supprimer les Transactions de la Société, @@ -7639,15 +7639,15 @@ Bank Credit Balance,Solde bancaire, Receivables,Créances, Payables,Dettes, Sales Orders to Bill,Commandes de vente à facture, -Purchase Orders to Bill,Commandes d'achat à facturer, +Purchase Orders to Bill,Commandes d'achat à facturer, New Sales Orders,Nouvelles Commandes Client, New Purchase Orders,Nouveaux Bons de Commande, Sales Orders to Deliver,Commandes de vente à livrer, -Purchase Orders to Receive,Commandes d'achat à recevoir, -New Purchase Invoice,Nouvelle facture d'achat, +Purchase Orders to Receive,Commandes d'achat à recevoir, +New Purchase Invoice,Nouvelle facture d'achat, New Quotations,Nouveaux Devis, Open Quotations,Citations ouvertes, -Purchase Orders Items Overdue,Articles de commandes d'achat en retard, +Purchase Orders Items Overdue,Articles de commandes d'achat en retard, Add Quote,Ajouter une Citation, Global Defaults,Valeurs par Défaut Globales, Default Company,Société par Défaut, @@ -7662,7 +7662,7 @@ Item Classification,Classification de l'Article, General Settings,Paramètres Généraux, Item Group Name,Nom du Groupe d'Article, Parent Item Group,Groupe d’Articles Parent, -Item Group Defaults,Groupe d'articles par défaut, +Item Group Defaults,Groupe d'articles par défaut, Item Tax,Taxe sur l'Article, Check this if you want to show in website,Cochez cette case si vous souhaitez afficher sur le site, Show this slideshow at the top of the page,Afficher ce diaporama en haut de la page, @@ -7723,7 +7723,7 @@ Website Item Group,Groupe d'Articles du Site Web, Cross Listing of Item in multiple groups,Liste Croisée d'Articles dans plusieurs groupes, Default settings for Shopping Cart,Paramètres par défaut pour le Panier d'Achat, Enable Shopping Cart,Activer Panier, -Display Settings,Paramètres d'affichage, +Display Settings,Paramètres d'affichage, Show Public Attachments,Afficher les Pièces Jointes Publiques, Show Price,Afficher le prix, Show Stock Availability,Afficher la disponibilité du stock, @@ -7756,7 +7756,7 @@ Tariff Number,Tarif, Delivery To,Livraison à, MAT-DN-.YYYY.-,MAT-DN-.YYYY.-, Is Return,Est un Retour, -Issue Credit Note,Note de crédit d'émission, +Issue Credit Note,Note de crédit d'émission, Return Against Delivery Note,Retour contre Bon de Livraison, Customer's Purchase Order No,Numéro bon de commande du client, Billing Address Name,Nom de l'Adresse de Facturation, @@ -7783,8 +7783,8 @@ Available Batch Qty at From Warehouse,Qté de Lot Disponible Depuis l'Entrepôt, Available Qty at From Warehouse,Qté Disponible Depuis l'Entrepôt, Delivery Settings,Paramètres de livraison, Dispatch Settings,Paramètres de répartition, -Dispatch Notification Template,Modèle de notification d'expédition, -Dispatch Notification Attachment,Pièce jointe de notification d'expédition, +Dispatch Notification Template,Modèle de notification d'expédition, +Dispatch Notification Attachment,Pièce jointe de notification d'expédition, Leave blank to use the standard Delivery Note format,Laissez vide pour utiliser le format de bon de livraison standard, Send with Attachment,Envoyer avec pièce jointe, Delay between Delivery Stops,Délai entre les arrêts de livraison, @@ -7793,7 +7793,7 @@ Visited,Visité, Order Information,Informations sur la commande, Contact Information,Informations de contact, Email sent to,Email Envoyé À, -Dispatch Information,Informations d'expédition, +Dispatch Information,Informations d'expédition, Estimated Arrival,Arrivée estimée, MAT-DT-.YYYY.-,MAT-DT-.YYYY.-, Initial Email Notification Sent,Notification initiale par e-mail envoyée, @@ -7804,10 +7804,10 @@ Total Estimated Distance,Distance totale estimée, Distance UOM,Distance UOM, Departure Time,Heure de départ, Delivery Stops,Étapes de Livraison, -Calculate Estimated Arrival Times,Calculer les heures d'arrivée estimées, -Use Google Maps Direction API to calculate estimated arrival times,Utiliser l'API Google Maps Direction pour calculer les heures d'arrivée estimées, -Optimize Route,Optimiser l'itinéraire, -Use Google Maps Direction API to optimize route,Utiliser l'API Google Maps Direction pour optimiser l'itinéraire, +Calculate Estimated Arrival Times,Calculer les heures d'arrivée estimées, +Use Google Maps Direction API to calculate estimated arrival times,Utiliser l'API Google Maps Direction pour calculer les heures d'arrivée estimées, +Optimize Route,Optimiser l'itinéraire, +Use Google Maps Direction API to optimize route,Utiliser l'API Google Maps Direction pour optimiser l'itinéraire, In Transit,En transit, Fulfillment User,Livreur, "A Product or a Service that is bought, sold or kept in stock.","Un Produit ou un Service qui est acheté, vendu ou conservé en stock.", @@ -7817,7 +7817,7 @@ Is Item from Hub,Est un article sur le Hub, Default Unit of Measure,Unité de Mesure par Défaut, Maintain Stock,Maintenir Stock, Standard Selling Rate,Prix de Vente Standard, -Auto Create Assets on Purchase,Création automatique d'actifs à l'achat, +Auto Create Assets on Purchase,Création automatique d'actifs à l'achat, Asset Naming Series,Nom de série de l'actif, Over Delivery/Receipt Allowance (%),Surlivrance / indemnité de réception (%), Barcodes,Codes-barres, @@ -7839,9 +7839,9 @@ Automatically Create New Batch,Créer un Nouveau Lot Automatiquement, Batch Number Series,Série de numéros de lots, "Example: ABCD.#####. If series is set and Batch No is not mentioned in transactions, then automatic batch number will be created based on this series. If you always want to explicitly mention Batch No for this item, leave this blank. Note: this setting will take priority over the Naming Series Prefix in Stock Settings.","Exemple: ABCD. #####. Si la série est définie et que le numéro de lot n'est pas mentionné dans les transactions, un numéro de lot sera automatiquement créé en avec cette série. Si vous préferez mentionner explicitement et systématiquement le numéro de lot pour cet article, laissez ce champ vide. Remarque: ce paramètre aura la priorité sur le préfixe de la série dans les paramètres de stock.", Has Expiry Date,A une date d'expiration, -Retain Sample,Conserver l'échantillon, -Max Sample Quantity,Quantité maximum d'échantillon, -Maximum sample quantity that can be retained,Quantité maximale d'échantillon pouvant être conservée, +Retain Sample,Conserver l'échantillon, +Max Sample Quantity,Quantité maximum d'échantillon, +Maximum sample quantity that can be retained,Quantité maximale d'échantillon pouvant être conservée, Has Serial No,A un N° de Série, Serial Number Series,Séries de Numéros de Série, "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.","Exemple:. ABCD ##### Si la série est définie et que le N° de série n'est pas mentionné dans les transactions, alors un numéro de série automatique sera créé basé sur cette série. Si vous voulez toujours mentionner explicitement les numéros de série pour ce produit. laissez ce champ vide.", @@ -7852,13 +7852,13 @@ Variant Based On,Variante Basée Sur, Item Attribute,Attribut de l'Article, "Sales, Purchase, Accounting Defaults","Valeurs par défaut pour les ventes, les achats et la comptabilité", Item Defaults,Paramètres par défaut de l'article, -"Purchase, Replenishment Details","Détails d'achat, de réapprovisionnement", +"Purchase, Replenishment Details","Détails d'achat, de réapprovisionnement", Is Purchase Item,Est Article d'Achat, Default Purchase Unit of Measure,Unité de Mesure par défaut à l'Achat, Minimum Order Qty,Qté de Commande Minimum, -Minimum quantity should be as per Stock UOM,La quantité minimale doit être conforme à l'UdM du stock, +Minimum quantity should be as per Stock UOM,La quantité minimale doit être conforme à l'UdM du stock, Average time taken by the supplier to deliver,Délai moyen de livraison par le fournisseur, -Is Customer Provided Item,L'article est-il fourni par le client?, +Is Customer Provided Item,L'article est-il fourni par le client?, Delivered by Supplier (Drop Ship),Livré par le Fournisseur (Expédition Directe), Supplier Items,Articles Fournisseur, Foreign Trade Details,Détails du Commerce Extérieur, @@ -7886,7 +7886,7 @@ Website Item Groups,Groupes d'Articles du Site Web, List this Item in multiple groups on the website.,Liste cet article dans plusieurs groupes sur le site., Copy From Item Group,Copier Depuis un Groupe d'Articles, Website Content,Contenu du site Web, -You can use any valid Bootstrap 4 markup in this field. It will be shown on your Item Page.,Vous pouvez utiliser n'importe quelle balise Bootstrap 4 valide dans ce champ. Il sera affiché sur votre page d'article., +You can use any valid Bootstrap 4 markup in this field. It will be shown on your Item Page.,Vous pouvez utiliser n'importe quelle balise Bootstrap 4 valide dans ce champ. Il sera affiché sur votre page d'article., Total Projected Qty,Qté Totale Prévue, Hub Publishing Details,Détails Publiés sur le Hub, Publish in Hub,Publier dans le Hub, @@ -7898,7 +7898,7 @@ Synced With Hub,Synchronisé avec le Hub, Item Alternative,Alternative à l'Article, Alternative Item Code,Code de l'article alternatif, Two-way,A double-sens, -Alternative Item Name,Nom de l'article alternatif, +Alternative Item Name,Nom de l'article alternatif, Attribute Name,Nom de l'Attribut, Numeric Values,Valeurs Numériques, From Range,Plage Initiale, @@ -7925,7 +7925,7 @@ Sales Defaults,Valeurs par défaut pour la vente, Default Selling Cost Center,Centre de Coût Vendeur par Défaut, Item Manufacturer,Fabricant d'Article, Item Price,Prix de l'Article, -Packing Unit,Unité d'emballage, +Packing Unit,Unité d'emballage, Quantity that must be bought or sold per UOM,Quantité à acheter ou à vendre par unité de mesure, Valid From ,Valide à Partir de, Valid Upto ,Valide Jusqu'au, @@ -8001,7 +8001,7 @@ Pick List Item,Élément de la liste de choix, Picked Qty,Quantité choisie, Price List Master,Données de Base des Listes de Prix, Price List Name,Nom de la Liste de Prix, -Price Not UOM Dependent,Prix non dépendant de l'UOM, +Price Not UOM Dependent,Prix non dépendant de l'UOM, Applicable for Countries,Applicable pour les Pays, Price List Country,Pays de la Liste des Prix, MAT-PRE-.YYYY.-,MAT-PRE-YYYY.-, @@ -8018,7 +8018,7 @@ Vehicle Date,Date du Véhicule, Received and Accepted,Reçus et Acceptés, Accepted Quantity,Quantité Acceptée, Rejected Quantity,Quantité Rejetée, -Sample Quantity,Quantité d'échantillon, +Sample Quantity,Quantité d'échantillon, Rate and Amount,Prix et Montant, MAT-QA-.YYYY.-,MAT-QA-YYYY.-, Report Date,Date du Rapport, @@ -8038,7 +8038,7 @@ Reading 7,Lecture 7, Reading 8,Lecture 8, Reading 9,Lecture 9, Reading 10,Lecture 10, -Quality Inspection Template Name,Nom du modèle d'inspection de la qualité, +Quality Inspection Template Name,Nom du modèle d'inspection de la qualité, Quick Stock Balance,Solde rapide des stocks, Available Quantity,quantité disponible, Distinct unit of an Item,Unité distincte d'un Article, @@ -8048,8 +8048,8 @@ Creation Document Type,Type de Document de Création, Creation Document No,N° du Document de Création, Creation Date,Date de Création, Creation Time,Date de Création, -Asset Details,Détails de l'actif, -Asset Status,Statut de l'actif, +Asset Details,Détails de l'actif, +Asset Status,Statut de l'actif, Delivery Document Type,Type de Document de Livraison, Delivery Document No,Numéro de Document de Livraison, Delivery Time,Heure de la Livraison, @@ -8064,13 +8064,13 @@ Out of AMC,Sur AMC, Warranty Period (Days),Période de Garantie (Jours), Serial No Details,Détails du N° de Série, MAT-STE-.YYYY.-,MAT-STE-.YYYY.-, -Stock Entry Type,Type d'entrée de stock, +Stock Entry Type,Type d'entrée de stock, Stock Entry (Outward GIT),Entrée de stock (GIT sortant), Material Consumption for Manufacture,Consommation de matériaux pour la production, Repack,Ré-emballer, Send to Subcontractor,Envoyer au sous-traitant, -Send to Warehouse,Envoyer à l'entrepôt, -Receive at Warehouse,Recevez à l'entrepôt, +Send to Warehouse,Envoyer à l'entrepôt, +Receive at Warehouse,Recevez à l'entrepôt, Delivery Note No,Bon de Livraison N°, Sales Invoice No,N° de la Facture de Vente, Purchase Receipt No,N° du Reçu d'Achat, @@ -8080,9 +8080,9 @@ For Quantity,Pour la Quantité, As per Stock UOM,Selon UDM du Stock, Including items for sub assemblies,Incluant les articles pour des sous-ensembles, Default Source Warehouse,Entrepôt Source par Défaut, -Source Warehouse Address,Adresse de l'entrepôt source, +Source Warehouse Address,Adresse de l'entrepôt source, Default Target Warehouse,Entrepôt Cible par Défaut, -Target Warehouse Address,Adresse de l'entrepôt cible, +Target Warehouse Address,Adresse de l'entrepôt cible, Update Rate and Availability,Mettre à Jour le Prix et la Disponibilité, Total Incoming Value,Valeur Entrante Totale, Total Outgoing Value,Valeur Sortante Totale, @@ -8102,7 +8102,7 @@ Subcontracted Item,Article sous-traité, Against Stock Entry,Contre entrée de stock, Stock Entry Child,Entrée de stock enfant, PO Supplied Item,PO article fourni, -Reference Purchase Receipt,Reçu d'achat de référence, +Reference Purchase Receipt,Reçu d'achat de référence, Stock Ledger Entry,Écriture du Livre d'Inventaire, Outgoing Rate,Taux Sortant, Actual Qty After Transaction,Qté Réelle Après Transaction, @@ -8126,9 +8126,9 @@ Default Stock UOM,UDM par Défaut des Articles, Sample Retention Warehouse,Entrepôt de stockage des échantillons, Default Valuation Method,Méthode de Valorisation par Défaut, Percentage you are allowed to receive or deliver more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to receive 110 units.,Pourcentage que vous êtes autorisé à recevoir ou à livrer en plus de la quantité commandée. Par exemple : Si vous avez commandé 100 unités et que votre allocation est de 10% alors que vous êtes autorisé à recevoir 110 unités., -Action if Quality inspection is not submitted,Action si l'inspection qualité n'est pas soumise, +Action if Quality inspection is not submitted,Action si l'inspection qualité n'est pas soumise, Show Barcode Field,Afficher Champ Code Barre, -Convert Item Description to Clean HTML,Convertir la description de l'élément pour nettoyer le code HTML, +Convert Item Description to Clean HTML,Convertir la description de l'élément pour nettoyer le code HTML, Auto insert Price List rate if missing,Insertion automatique du taux de la Liste de Prix si manquante, Allow Negative Stock,Autoriser un Stock Négatif, Automatically Set Serial Nos based on FIFO,Régler Automatiquement les Nos de Série basés sur FIFO, @@ -8162,7 +8162,7 @@ Service Level Agreement Fulfilled,Contrat de niveau de service rempli, Ongoing,En cours, Resolution By,Résolution de, Resolution By Variance,Résolution par variance, -Service Level Agreement Creation,Création d'un contrat de niveau de service, +Service Level Agreement Creation,Création d'un contrat de niveau de service, Mins to First Response,Minutes avant la Première Réponse, First Responded On,Première Réponse Le, Resolution Details,Détails de la Résolution, @@ -8171,7 +8171,7 @@ Opening Time,Horaire d'Ouverture, Resolution Date,Date de Résolution, Via Customer Portal,Via le portail client, Support Team,Équipe de Support, -Issue Priority,Priorité d'émission, +Issue Priority,Priorité d'émission, Service Day,Jour de service, Workday,Journée de travail, Holiday List (ignored during SLA calculation),Liste de jours fériés (ignorée lors du calcul du contrat de niveau de service), @@ -8182,7 +8182,7 @@ Support Hours,Heures de Support, Support and Resolution,Support et résolution, Default Service Level Agreement,Contrat de niveau de service par défaut, Entity,Entité, -Agreement Details,Détails de l'accord, +Agreement Details,Détails de l'accord, Response and Resolution Time,Temps de réponse et de résolution, Service Level Priority,Priorité de niveau de service, Response Time,Temps de réponse, @@ -8202,11 +8202,11 @@ Post Description Key,Clé de description du message, Link Options,Options du lien, Source DocType,DocType source, Result Title Field,Champ du titre du résultat, -Result Preview Field,Champ d'aperçu du résultat, +Result Preview Field,Champ d'aperçu du résultat, Result Route Field,Champ du lien du résultat, Service Level Agreements,Accords de Niveau de Service, Track Service Level Agreement,Suivi du contrat de niveau de service, -Allow Resetting Service Level Agreement,Autoriser la réinitialisation de l'accord de niveau de service, +Allow Resetting Service Level Agreement,Autoriser la réinitialisation de l'accord de niveau de service, Close Issue After Days,Nbre de jours avant de fermer le ticket, Auto close Issue after 7 days,Fermer automatiquement le ticket après 7 jours, Support Portal,Portail du support, @@ -8263,12 +8263,12 @@ Course wise Assessment Report,Rapport d'Évaluation par Cours, Customer Acquisition and Loyalty,Acquisition et Fidélisation des Clients, Customer Credit Balance,Solde de Crédit des Clients, Customer Ledger Summary,Récapitulatif client, -Customer-wise Item Price,Prix de l'article par client, +Customer-wise Item Price,Prix de l'article par client, Customers Without Any Sales Transactions,Clients sans transactions de vente, Daily Timesheet Summary,Récapitulatif Quotidien des Feuilles de Présence, Daily Work Summary Replies,Réponses au récapitulatif de travail quotidien, DATEV,DATEV, -Delayed Item Report,Rapport d'élément retardé, +Delayed Item Report,Rapport d'élément retardé, Delayed Order Report,Rapport de commande retardé, Delivered Items To Be Billed,Articles Livrés à Facturer, Delivery Note Trends,Tendance des Bordereaux de Livraisons, @@ -8305,7 +8305,7 @@ Item Price Stock,Stock et prix de l'article, Item Prices,Prix des Articles, Item Shortage Report,Rapport de Rupture de Stock d'Article, Project Quantity,Quantité de Projet, -Item Variant Details,Détails de la variante de l'article, +Item Variant Details,Détails de la variante de l'article, Item-wise Price List Rate,Taux de la Liste des Prix par Article, Item-wise Purchase History,Historique d'Achats par Article, Item-wise Purchase Register,Registre des Achats par Article, @@ -8395,11 +8395,11 @@ Supplier-Wise Sales Analytics,Analyse des Ventes par Fournisseur, Support Hour Distribution,Répartition des Heures de Support, TDS Computation Summary,Résumé des calculs TDS, TDS Payable Monthly,TDS Payable Monthly, -Territory Target Variance Based On Item Group,Écart de cible de territoire basé sur un groupe d'articles, +Territory Target Variance Based On Item Group,Écart de cible de territoire basé sur un groupe d'articles, Territory-wise Sales,Ventes par territoire, Total Stock Summary,Récapitulatif de l'Inventaire Total, Trial Balance,Balance Générale, -Trial Balance (Simple),Balance d'essai (simple), +Trial Balance (Simple),Balance d'essai (simple), Trial Balance for Party,Balance Auxiliaire, Unpaid Expense Claim,Note de Frais Impayée, Warehouse wise Item Balance Age and Value,Balance des articles par entrepôt, diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 024aa6f31d..88a6dc042c 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -7,7 +7,6 @@ import frappe.share from frappe import _ from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_datetime, get_link_to_form from erpnext.controllers.status_updater import StatusUpdater -from erpnext.accounts.utils import get_fiscal_year from six import string_types @@ -161,19 +160,19 @@ class TransactionBase(StatusUpdater): if not (self.get("update_stock") or self.get("is_pos")): return - fiscal_year = get_fiscal_year(self.get('posting_date'), as_dict=True).name + for item in self.get('items'): + last_transaction_time = frappe.db.sql(""" + select MAX(timestamp(posting_date, posting_time)) as posting_time + from `tabStock Ledger Entry` + where docstatus = 1 and item_code = %s """, (item.item_code))[0][0] - last_transaction_time = frappe.db.sql(""" - select MAX(timestamp(posting_date, posting_time)) as posting_time - from `tabStock Ledger Entry` - where docstatus = 1""")[0][0] + cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") - cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") - - if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time): - frappe.throw(_("""Posting timestamp of current transaction - must be after last Stock transaction's timestamp which is {0}""").format(frappe.bold(last_transaction_time)), - title=_("Backdated Stock Entry")) + if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time): + msg = _("Last Stock Transaction for item {0} was on {1}.").format(frappe.bold(item.item_code), frappe.bold(last_transaction_time)) + msg += "

    " + _("Stock Transactions for Item {0} cannot be posted before this time.").format(frappe.bold(item.item_code)) + msg += "

    " + _("Please remove this item and try to submit again or update the posting time.") + frappe.throw(msg, title=_("Backdated Stock Entry")) def delete_events(ref_type, ref_name): events = frappe.db.sql_list(""" SELECT diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html index f09021412b..0126b59c64 100644 --- a/erpnext/www/all-products/index.html +++ b/erpnext/www/all-products/index.html @@ -11,7 +11,7 @@
    diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html index cdc71412c4..dc9b6d80fb 100644 --- a/erpnext/www/lms/content.html +++ b/erpnext/www/lms/content.html @@ -59,7 +59,7 @@ {% macro title() %}
    - Back to Course + {{_('Back to Course')}}
    @@ -69,15 +69,15 @@ {% macro navigation() %} {% if previous %} - Previous + {{_('Previous')}} {% else %} - Back to Course + {{ _('Back to Course') }} {% endif %} {% if next %} - + {% else %} - + {% endif %} {% endmacro %} @@ -86,7 +86,7 @@ {{ title() }}
    {% if content.duration %} - {{ content.duration }} Mins + {{ content.duration }} {{_('Mins')}} {% endif %} {% if content.publish_date and content.duration%} @@ -94,7 +94,7 @@ {% endif %} {% if content.publish_date %} - Published on {{ content.publish_date.strftime('%d, %b %Y') }} + {{_('Published on')}} {{ content.publish_date.strftime('%d, %b %Y') }} {% endif %}
    @@ -109,13 +109,13 @@ {{ title() }}
    {% if content.author or content.publish_date %} - Published + {{_('Published')}} {% endif %} {% if content.author %} - by {{ content.author }} + {{_('by')}} {{ content.author }} {% endif %} {% if content.publish_date %} - on {{ content.publish_date.strftime('%d, %b %Y') }} + {{_('on')}} {{ content.publish_date.strftime('%d, %b %Y') }} {% endif %}
    @@ -205,4 +205,4 @@ {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/course.html b/erpnext/www/lms/course.html index f2fd9363e8..0d70ed5cef 100644 --- a/erpnext/www/lms/course.html +++ b/erpnext/www/lms/course.html @@ -72,11 +72,11 @@ {% if has_access %} diff --git a/erpnext/www/lms/index.html b/erpnext/www/lms/index.html index ffb4419f36..7ce3521273 100644 --- a/erpnext/www/lms/index.html +++ b/erpnext/www/lms/index.html @@ -45,7 +45,7 @@

    {{ education_settings.description }}

    {% if frappe.session.user == 'Guest' %} - Sign Up + {{_('Sign Up')}} {% endif %}

    diff --git a/erpnext/www/lms/macros/card.html b/erpnext/www/lms/macros/card.html index 076061d41b..dc8fc5c72c 100644 --- a/erpnext/www/lms/macros/card.html +++ b/erpnext/www/lms/macros/card.html @@ -15,8 +15,8 @@ {% if has_access or program.intro_video%} {% endif %} diff --git a/erpnext/www/lms/macros/hero.html b/erpnext/www/lms/macros/hero.html index 66bb861c46..94f239eb8e 100644 --- a/erpnext/www/lms/macros/hero.html +++ b/erpnext/www/lms/macros/hero.html @@ -2,16 +2,16 @@

    {{ title }}

    {{ description or ''}}

    {% if frappe.session.user == 'Guest' %} - Sign Up + {{_('Sign Up')}} {% elif not has_access %} - + {% endif %}

    @@ -28,7 +28,7 @@ let btn = document.getElementById('enroll'); btn.disbaled = true; - btn.innerText = 'Enrolling...' + btn.innerText = __('Enrolling...') let opts = { method: 'erpnext.education.utils.enroll_in_program', @@ -44,7 +44,7 @@ window.location.reload() } }) - success_dialog.set_message('You have successfully enrolled for the program '); + success_dialog.set_message(__('You have successfully enrolled for the program ')); success_dialog.$message.show() success_dialog.show(); btn.disbaled = false; diff --git a/erpnext/www/lms/profile.html b/erpnext/www/lms/profile.html index 9508daedb7..5755dfe6d8 100644 --- a/erpnext/www/lms/profile.html +++ b/erpnext/www/lms/profile.html @@ -30,7 +30,7 @@ @@ -43,11 +43,11 @@

    {{ student.first_name }} {{ student.last_name or '' }}

    @@ -61,4 +61,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/program.html b/erpnext/www/lms/program.html index 271b7813bb..7ad618630a 100644 --- a/erpnext/www/lms/program.html +++ b/erpnext/www/lms/program.html @@ -55,11 +55,11 @@ {% if has_access and progress[course.name] %} {% endif %} diff --git a/erpnext/www/lms/topic.html b/erpnext/www/lms/topic.html index 1f0d187664..cd24616cd4 100644 --- a/erpnext/www/lms/topic.html +++ b/erpnext/www/lms/topic.html @@ -23,13 +23,13 @@ {% if has_access %} diff --git a/erpnext/www/support/__init__.py b/erpnext/www/support/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html new file mode 100644 index 0000000000..93da503dbb --- /dev/null +++ b/erpnext/www/support/index.html @@ -0,0 +1,58 @@ +{% extends "templates/web.html" %} + +{% block content %} +
    +
    +
    +

    {{ greeting_title or _("We're here to help!") }}

    + {% if greeting_subtitle %} +

    {{ greeting_subtitle }}

    + {% endif %} +
    +
    +
    + +{% if favorite_article_list %} +
    +
    +

    {{ _("Frequently Read Articles") }}

    +
    + {% for favorite_article in favorite_article_list %} +
    +
    +
    +
    + {{ favorite_article['category'] }}
    +

    {{ favorite_article['title'] }}

    +

    {{ favorite_article['description'] }}

    +
    + +
    +
    + {% endfor %} +
    +
    +
    +{% endif %} + +{% if help_article_list %} +
    +
    +

    {{ _("Help Articles") }}

    +
    + {% for item in help_article_list %} +
    +
    {{ item['category'].name }}
    +
    + {% for article in item['articles'] %} + {{ article.title }} + {% endfor %} +
    +
    + {% endfor %} +
    +
    +
    +{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/erpnext/www/support/index.py b/erpnext/www/support/index.py new file mode 100644 index 0000000000..5d267430c1 --- /dev/null +++ b/erpnext/www/support/index.py @@ -0,0 +1,74 @@ +from __future__ import unicode_literals +import frappe + +def get_context(context): + context.no_cache = 1 + context.align_greeting = '' + setting = frappe.get_doc("Support Settings") + + context.greeting_title = setting.greeting_title + context.greeting_subtitle = setting.greeting_subtitle + + # Support content + favorite_articles = get_favorite_articles_by_page_view() + if len(favorite_articles) < 6: + name_list = [] + if favorite_articles: + for article in favorite_articles: + name_list.append(article.name) + for record in (frappe.get_all("Help Article", + fields=["title", "content", "route", "category"], + filters={"name": ['not in', tuple(name_list)], "published": 1}, + order_by="creation desc", limit=(6-len(favorite_articles)))): + favorite_articles.append(record) + + context.favorite_article_list = get_favorite_articles(favorite_articles) + context.help_article_list = get_help_article_list() + +def get_favorite_articles_by_page_view(): + return frappe.db.sql( + """ + SELECT + t1.name as name, + t1.title as title, + t1.content as content, + t1.route as route, + t1.category as category, + count(t1.route) as count + FROM `tabHelp Article` AS t1 + INNER JOIN + `tabWeb Page View` AS t2 + ON t1.route = t2.path + WHERE t1.published = 1 + GROUP BY route + ORDER BY count DESC + LIMIT 6; + """, as_dict=True) + +def get_favorite_articles(favorite_articles): + favorite_article_list=[] + for article in favorite_articles: + description = frappe.utils.strip_html(article.content) + if len(description) > 120: + description = description[:120] + '...' + favorite_article_dict = { + 'title': article.title, + 'description': description, + 'route': article.route, + 'category': article.category, + } + favorite_article_list.append(favorite_article_dict) + return favorite_article_list + +def get_help_article_list(): + help_article_list=[] + category_list = frappe.get_all("Help Category", fields="name") + for category in category_list: + help_articles = frappe.get_all("Help Article", fields="*", filters={"category": category.name, "published": 1}, order_by="modified desc", limit=5) + if help_articles: + help_aricles_per_caetgory = { + 'category': category, + 'articles': help_articles, + } + help_article_list.append(help_aricles_per_caetgory) + return help_article_list \ No newline at end of file