Merge branch 'develop' into asset-delete-fix
This commit is contained in:
commit
41034f760d
@ -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"));
|
||||
|
@ -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'));
|
||||
|
@ -68,6 +68,9 @@ class OpeningInvoiceCreationTool(Document):
|
||||
if not self.company:
|
||||
frappe.throw(_("Please select the Company"))
|
||||
|
||||
company_details = frappe.get_cached_value('Company', self.company,
|
||||
["default_currency", "default_letter_head"], as_dict=1) or {}
|
||||
|
||||
for row in self.invoices:
|
||||
if not row.qty:
|
||||
row.qty = 1.0
|
||||
@ -99,6 +102,12 @@ class OpeningInvoiceCreationTool(Document):
|
||||
if not args:
|
||||
continue
|
||||
|
||||
if company_details:
|
||||
args.update({
|
||||
"currency": company_details.get("default_currency"),
|
||||
"letter_head": company_details.get("default_letter_head")
|
||||
})
|
||||
|
||||
doc = frappe.get_doc(args).insert()
|
||||
doc.submit()
|
||||
names.append(doc.name)
|
||||
@ -172,8 +181,7 @@ class OpeningInvoiceCreationTool(Document):
|
||||
"due_date": row.due_date,
|
||||
"posting_date": row.posting_date,
|
||||
frappe.scrub(party_type): row.party,
|
||||
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
|
||||
"currency": frappe.get_cached_value('Company', self.company, "default_currency")
|
||||
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice"
|
||||
})
|
||||
|
||||
accounting_dimension = get_accounting_dimensions()
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -393,6 +393,8 @@ class Asset(AccountsController):
|
||||
row.expected_value_after_useful_life = asset_value_after_full_schedule
|
||||
|
||||
def validate_cancellation(self):
|
||||
if self.status in ("In Maintenance", "Out of Order"):
|
||||
frappe.throw(_("There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset."))
|
||||
if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
|
||||
frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status))
|
||||
|
||||
|
@ -33,7 +33,7 @@ def validate_filters(filters):
|
||||
frappe.throw(_("{0} is mandatory").format(f))
|
||||
|
||||
if not frappe.db.exists("Fiscal Year", filters.get("fiscal_year")):
|
||||
frappe.throw(_("Fiscal Year: {0} does not exists").format(filters.get("fiscal_year")))
|
||||
frappe.throw(_("Fiscal Year {0} Does Not Exist").format(filters.get("fiscal_year")))
|
||||
|
||||
if filters.get("based_on") == filters.get("group_by"):
|
||||
frappe.throw(_("'Based On' and 'Group By' can not be same"))
|
||||
|
@ -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"));
|
||||
|
@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('TaxJar Settings', {
|
||||
is_sandbox: (frm) => {
|
||||
frm.toggle_reqd("api_key", !frm.doc.is_sandbox);
|
||||
frm.toggle_reqd("sandbox_api_key", frm.doc.is_sandbox);
|
||||
}
|
||||
});
|
@ -0,0 +1,110 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2017-06-15 08:21:24.624315",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"is_sandbox",
|
||||
"taxjar_calculate_tax",
|
||||
"taxjar_create_transactions",
|
||||
"credentials",
|
||||
"api_key",
|
||||
"cb_keys",
|
||||
"sandbox_api_key",
|
||||
"configuration",
|
||||
"tax_account_head",
|
||||
"configuration_cb",
|
||||
"shipping_account_head"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "credentials",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Credentials"
|
||||
},
|
||||
{
|
||||
"fieldname": "api_key",
|
||||
"fieldtype": "Password",
|
||||
"in_list_view": 1,
|
||||
"label": "Live API Key",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "configuration",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Configuration"
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_account_head",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Tax Account Head",
|
||||
"options": "Account",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "shipping_account_head",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Shipping Account Head",
|
||||
"options": "Account",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_sandbox",
|
||||
"fieldtype": "Check",
|
||||
"label": "Sandbox Mode"
|
||||
},
|
||||
{
|
||||
"fieldname": "sandbox_api_key",
|
||||
"fieldtype": "Password",
|
||||
"label": "Sandbox API Key"
|
||||
},
|
||||
{
|
||||
"fieldname": "configuration_cb",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "taxjar_create_transactions",
|
||||
"fieldtype": "Check",
|
||||
"label": "Create TaxJar Transaction"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "taxjar_calculate_tax",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Tax Calculation"
|
||||
},
|
||||
{
|
||||
"fieldname": "cb_keys",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-30 04:38:03.311089",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "TaxJar Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -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 TaxJarSettings(Document):
|
||||
pass
|
@ -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 TestTaxJarSettings(unittest.TestCase):
|
||||
pass
|
251
erpnext/erpnext_integrations/taxjar_integration.py
Normal file
251
erpnext/erpnext_integrations/taxjar_integration.py
Normal file
@ -0,0 +1,251 @@
|
||||
import traceback
|
||||
|
||||
import pycountry
|
||||
import taxjar
|
||||
|
||||
import frappe
|
||||
from erpnext import get_default_company
|
||||
from frappe import _
|
||||
from frappe.contacts.doctype.address.address import get_company_address
|
||||
|
||||
TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head")
|
||||
SHIP_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "shipping_account_head")
|
||||
TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
|
||||
TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax")
|
||||
SUPPORTED_COUNTRY_CODES = ["AT", "AU", "BE", "BG", "CA", "CY", "CZ", "DE", "DK", "EE", "ES", "FI",
|
||||
"FR", "GB", "GR", "HR", "HU", "IE", "IT", "LT", "LU", "LV", "MT", "NL", "PL", "PT", "RO",
|
||||
"SE", "SI", "SK", "US"]
|
||||
|
||||
|
||||
def get_client():
|
||||
taxjar_settings = frappe.get_single("TaxJar Settings")
|
||||
|
||||
if not taxjar_settings.is_sandbox:
|
||||
api_key = taxjar_settings.api_key and taxjar_settings.get_password("api_key")
|
||||
api_url = taxjar.DEFAULT_API_URL
|
||||
else:
|
||||
api_key = taxjar_settings.sandbox_api_key and taxjar_settings.get_password("sandbox_api_key")
|
||||
api_url = taxjar.SANDBOX_API_URL
|
||||
|
||||
if api_key and api_url:
|
||||
return taxjar.Client(api_key=api_key, api_url=api_url)
|
||||
|
||||
|
||||
def create_transaction(doc, method):
|
||||
"""Create an order transaction in TaxJar"""
|
||||
|
||||
if not TAXJAR_CREATE_TRANSACTIONS:
|
||||
return
|
||||
|
||||
client = get_client()
|
||||
|
||||
if not client:
|
||||
return
|
||||
|
||||
sales_tax = sum([tax.tax_amount for tax in doc.taxes if tax.account_head == TAX_ACCOUNT_HEAD])
|
||||
|
||||
if not sales_tax:
|
||||
return
|
||||
|
||||
tax_dict = get_tax_data(doc)
|
||||
|
||||
if not tax_dict:
|
||||
return
|
||||
|
||||
tax_dict['transaction_id'] = doc.name
|
||||
tax_dict['transaction_date'] = frappe.utils.today()
|
||||
tax_dict['sales_tax'] = sales_tax
|
||||
tax_dict['amount'] = doc.total + tax_dict['shipping']
|
||||
|
||||
try:
|
||||
client.create_order(tax_dict)
|
||||
except taxjar.exceptions.TaxJarResponseError as err:
|
||||
frappe.throw(_(sanitize_error_response(err)))
|
||||
except Exception as ex:
|
||||
print(traceback.format_exc(ex))
|
||||
|
||||
|
||||
def delete_transaction(doc, method):
|
||||
"""Delete an existing TaxJar order transaction"""
|
||||
|
||||
if not TAXJAR_CREATE_TRANSACTIONS:
|
||||
return
|
||||
|
||||
client = get_client()
|
||||
|
||||
if not client:
|
||||
return
|
||||
|
||||
client.delete_order(doc.name)
|
||||
|
||||
|
||||
def get_tax_data(doc):
|
||||
from_address = get_company_address_details(doc)
|
||||
from_shipping_state = from_address.get("state")
|
||||
from_country_code = frappe.db.get_value("Country", from_address.country, "code")
|
||||
from_country_code = from_country_code.upper()
|
||||
|
||||
to_address = get_shipping_address_details(doc)
|
||||
to_shipping_state = to_address.get("state")
|
||||
to_country_code = frappe.db.get_value("Country", to_address.country, "code")
|
||||
to_country_code = to_country_code.upper()
|
||||
|
||||
if to_country_code not in SUPPORTED_COUNTRY_CODES:
|
||||
return
|
||||
|
||||
shipping = sum([tax.tax_amount for tax in doc.taxes if tax.account_head == SHIP_ACCOUNT_HEAD])
|
||||
|
||||
if to_shipping_state is not None:
|
||||
to_shipping_state = get_iso_3166_2_state_code(to_address)
|
||||
|
||||
tax_dict = {
|
||||
'from_country': from_country_code,
|
||||
'from_zip': from_address.pincode,
|
||||
'from_state': from_shipping_state,
|
||||
'from_city': from_address.city,
|
||||
'from_street': from_address.address_line1,
|
||||
'to_country': to_country_code,
|
||||
'to_zip': to_address.pincode,
|
||||
'to_city': to_address.city,
|
||||
'to_street': to_address.address_line1,
|
||||
'to_state': to_shipping_state,
|
||||
'shipping': shipping,
|
||||
'amount': doc.net_total
|
||||
}
|
||||
|
||||
return tax_dict
|
||||
|
||||
|
||||
def set_sales_tax(doc, method):
|
||||
if not TAXJAR_CALCULATE_TAX:
|
||||
return
|
||||
|
||||
if not doc.items:
|
||||
return
|
||||
|
||||
# if the party is exempt from sales tax, then set all tax account heads to zero
|
||||
sales_tax_exempted = hasattr(doc, "exempt_from_sales_tax") and doc.exempt_from_sales_tax \
|
||||
or frappe.db.has_column("Customer", "exempt_from_sales_tax") and frappe.db.get_value("Customer", doc.customer, "exempt_from_sales_tax")
|
||||
|
||||
if sales_tax_exempted:
|
||||
for tax in doc.taxes:
|
||||
if tax.account_head == TAX_ACCOUNT_HEAD:
|
||||
tax.tax_amount = 0
|
||||
break
|
||||
|
||||
doc.run_method("calculate_taxes_and_totals")
|
||||
return
|
||||
|
||||
tax_dict = get_tax_data(doc)
|
||||
|
||||
if not tax_dict:
|
||||
# Remove existing tax rows if address is changed from a taxable state/country
|
||||
setattr(doc, "taxes", [tax for tax in doc.taxes if tax.account_head != TAX_ACCOUNT_HEAD])
|
||||
return
|
||||
|
||||
tax_data = validate_tax_request(tax_dict)
|
||||
|
||||
if tax_data is not None:
|
||||
if not tax_data.amount_to_collect:
|
||||
setattr(doc, "taxes", [tax for tax in doc.taxes if tax.account_head != TAX_ACCOUNT_HEAD])
|
||||
elif tax_data.amount_to_collect > 0:
|
||||
# Loop through tax rows for existing Sales Tax entry
|
||||
# If none are found, add a row with the tax amount
|
||||
for tax in doc.taxes:
|
||||
if tax.account_head == TAX_ACCOUNT_HEAD:
|
||||
tax.tax_amount = tax_data.amount_to_collect
|
||||
|
||||
doc.run_method("calculate_taxes_and_totals")
|
||||
break
|
||||
else:
|
||||
doc.append("taxes", {
|
||||
"charge_type": "Actual",
|
||||
"description": "Sales Tax",
|
||||
"account_head": TAX_ACCOUNT_HEAD,
|
||||
"tax_amount": tax_data.amount_to_collect
|
||||
})
|
||||
|
||||
doc.run_method("calculate_taxes_and_totals")
|
||||
|
||||
|
||||
def validate_tax_request(tax_dict):
|
||||
"""Return the sales tax that should be collected for a given order."""
|
||||
|
||||
client = get_client()
|
||||
|
||||
if not client:
|
||||
return
|
||||
|
||||
try:
|
||||
tax_data = client.tax_for_order(tax_dict)
|
||||
except taxjar.exceptions.TaxJarResponseError as err:
|
||||
frappe.throw(_(sanitize_error_response(err)))
|
||||
else:
|
||||
return tax_data
|
||||
|
||||
|
||||
def get_company_address_details(doc):
|
||||
"""Return default company address details"""
|
||||
|
||||
company_address = get_company_address(get_default_company()).company_address
|
||||
|
||||
if not company_address:
|
||||
frappe.throw(_("Please set a default company address"))
|
||||
|
||||
company_address = frappe.get_doc("Address", company_address)
|
||||
return company_address
|
||||
|
||||
|
||||
def get_shipping_address_details(doc):
|
||||
"""Return customer shipping address details"""
|
||||
|
||||
if doc.shipping_address_name:
|
||||
shipping_address = frappe.get_doc("Address", doc.shipping_address_name)
|
||||
else:
|
||||
shipping_address = get_company_address_details(doc)
|
||||
|
||||
return shipping_address
|
||||
|
||||
|
||||
def get_iso_3166_2_state_code(address):
|
||||
country_code = frappe.db.get_value("Country", address.get("country"), "code")
|
||||
|
||||
error_message = _("""{0} is not a valid state! Check for typos or enter the ISO code for your state.""").format(address.get("state"))
|
||||
state = address.get("state").upper().strip()
|
||||
|
||||
# The max length for ISO state codes is 3, excluding the country code
|
||||
if len(state) <= 3:
|
||||
# PyCountry returns state code as {country_code}-{state-code} (e.g. US-FL)
|
||||
address_state = (country_code + "-" + state).upper()
|
||||
|
||||
states = pycountry.subdivisions.get(country_code=country_code.upper())
|
||||
states = [pystate.code for pystate in states]
|
||||
|
||||
if address_state in states:
|
||||
return state
|
||||
|
||||
frappe.throw(_(error_message))
|
||||
else:
|
||||
try:
|
||||
lookup_state = pycountry.subdivisions.lookup(state)
|
||||
except LookupError:
|
||||
frappe.throw(_(error_message))
|
||||
else:
|
||||
return lookup_state.code.split('-')[1]
|
||||
|
||||
|
||||
def sanitize_error_response(response):
|
||||
response = response.full_response.get("detail")
|
||||
response = response.replace("_", " ")
|
||||
|
||||
sanitized_responses = {
|
||||
"to zip": "Zipcode",
|
||||
"to city": "City",
|
||||
"to state": "State",
|
||||
"to country": "Country"
|
||||
}
|
||||
|
||||
for k, v in sanitized_responses.items():
|
||||
response = response.replace(k, v)
|
||||
|
||||
return response
|
@ -234,8 +234,15 @@ doc_events = {
|
||||
"validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products"
|
||||
},
|
||||
"Sales Invoice": {
|
||||
"on_submit": ["erpnext.regional.create_transaction_log", "erpnext.regional.italy.utils.sales_invoice_on_submit"],
|
||||
"on_cancel": "erpnext.regional.italy.utils.sales_invoice_on_cancel",
|
||||
"on_submit": [
|
||||
"erpnext.regional.create_transaction_log",
|
||||
"erpnext.regional.italy.utils.sales_invoice_on_submit",
|
||||
"erpnext.erpnext_integrations.taxjar_integration.create_transaction"
|
||||
],
|
||||
"on_cancel": [
|
||||
"erpnext.regional.italy.utils.sales_invoice_on_cancel",
|
||||
"erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
|
||||
],
|
||||
"on_trash": "erpnext.regional.check_deletion_permission"
|
||||
},
|
||||
"Purchase Invoice": {
|
||||
@ -261,6 +268,9 @@ doc_events = {
|
||||
},
|
||||
"Email Unsubscribe": {
|
||||
"after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient"
|
||||
},
|
||||
('Quotation', 'Sales Order', 'Sales Invoice'): {
|
||||
'validate': ["erpnext.erpnext_integrations.taxjar_integration.set_sales_tax"]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"));
|
||||
|
15
erpnext/hr/doctype/job_applicant/job_applicant_list.js
Normal file
15
erpnext/hr/doctype/job_applicant/job_applicant_list.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
|
||||
frappe.listview_settings['Job Applicant'] = {
|
||||
add_fields: ["company", "designation", "job_applicant", "status"],
|
||||
get_indicator: function (doc) {
|
||||
if (doc.status == "Accepted") {
|
||||
return [__(doc.status), "green", "status,=," + doc.status];
|
||||
} else if (["Open", "Replied"].includes(doc.status)) {
|
||||
return [__(doc.status), "orange", "status,=," + doc.status];
|
||||
} else if (["Hold", "Rejected"].includes(doc.status)) {
|
||||
return [__(doc.status), "red", "status,=," + doc.status];
|
||||
}
|
||||
}
|
||||
};
|
@ -30,7 +30,6 @@
|
||||
{
|
||||
"fieldname": "job_applicant",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Job Applicant",
|
||||
"options": "Job Applicant",
|
||||
"print_hide": 1,
|
||||
@ -161,7 +160,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-31 02:40:33.650728",
|
||||
"modified": "2020-06-25 00:56:24.756395",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Job Offer",
|
||||
|
15
erpnext/hr/doctype/job_offer/job_offer_list.js
Normal file
15
erpnext/hr/doctype/job_offer/job_offer_list.js
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
|
||||
frappe.listview_settings['Job Offer'] = {
|
||||
add_fields: ["company", "designation", "job_applicant", "status"],
|
||||
get_indicator: function (doc) {
|
||||
if (doc.status == "Accepted") {
|
||||
return [__(doc.status), "green", "status,=," + doc.status];
|
||||
} else if (doc.status == "Awaiting Response") {
|
||||
return [__(doc.status), "orange", "status,=," + doc.status];
|
||||
} else if (doc.status == "Rejected") {
|
||||
return [__(doc.status), "red", "status,=," + doc.status];
|
||||
}
|
||||
}
|
||||
};
|
@ -235,8 +235,10 @@ def make_repayment_entry(loan, applicant_type, applicant, loan_type, company, as
|
||||
@frappe.whitelist()
|
||||
def create_loan_security_unpledge(loan, applicant_type, applicant, company, as_dict=1):
|
||||
loan_security_pledge_details = frappe.db.sql("""
|
||||
SELECT p.parent, p.loan_security, p.qty as qty FROM `tabLoan Security Pledge` lsp , `tabPledge` p
|
||||
SELECT p.loan_security, sum(p.qty) as qty
|
||||
FROM `tabLoan Security Pledge` lsp , `tabPledge` p
|
||||
WHERE p.parent = lsp.name AND lsp.loan = %s AND lsp.docstatus = 1
|
||||
GROUP BY p.loan_security
|
||||
""",(loan), as_dict=1)
|
||||
|
||||
unpledge_request = frappe.new_doc("Loan Security Unpledge")
|
||||
|
@ -116,7 +116,7 @@ class LoanRepayment(AccountsController):
|
||||
def allocate_amounts(self, paid_entries):
|
||||
self.set('repayment_details', [])
|
||||
self.principal_amount_paid = 0
|
||||
interest_paid = 0
|
||||
interest_paid = self.amount_paid - self.penalty_amount
|
||||
|
||||
if self.amount_paid - self.penalty_amount > 0 and paid_entries:
|
||||
interest_paid = self.amount_paid - self.penalty_amount
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "LM-LSP-.####",
|
||||
"creation": "2019-09-03 18:20:31.382887",
|
||||
"doctype": "DocType",
|
||||
@ -46,6 +47,7 @@
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Loan Security Price",
|
||||
"options": "Company:company:default_currency",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -79,7 +81,8 @@
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"modified": "2019-10-26 09:46:46.069667",
|
||||
"links": [],
|
||||
"modified": "2020-06-11 03:41:33.900340",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Loan Management",
|
||||
"name": "Loan Security Price",
|
||||
|
@ -19,7 +19,9 @@ def update_shortfall_status(loan, security_value):
|
||||
return
|
||||
|
||||
if security_value >= loan_security_shortfall.shortfall_amount:
|
||||
frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, "status", "Completed")
|
||||
frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, {
|
||||
"status": "Completed",
|
||||
"shortfall_value": loan_security_shortfall.shortfall_amount})
|
||||
else:
|
||||
frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name,
|
||||
"shortfall_amount", loan_security_shortfall.shortfall_amount - security_value)
|
||||
|
@ -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");
|
||||
|
@ -98,11 +98,17 @@ class ProductionPlan(Document):
|
||||
elif self.get_items_from == "Material Request":
|
||||
self.get_mr_items()
|
||||
|
||||
def get_so_mr_list(self, field, table):
|
||||
"""Returns a list of Sales Orders or Material Requests from the respective tables"""
|
||||
so_mr_list = [d.get(field) for d in self.get(table) if d.get(field)]
|
||||
return so_mr_list
|
||||
|
||||
def get_so_items(self):
|
||||
so_list = [d.sales_order for d in self.sales_orders if d.sales_order]
|
||||
if not so_list:
|
||||
msgprint(_("Please enter Sales Orders in the above table"))
|
||||
return []
|
||||
# Check for empty table or empty rows
|
||||
if not self.get("sales_orders") or not self.get_so_mr_list("sales_order", "sales_orders"):
|
||||
frappe.throw(_("Please fill the Sales Orders table"), title=_("Sales Orders Required"))
|
||||
|
||||
so_list = self.get_so_mr_list("sales_order", "sales_orders")
|
||||
|
||||
item_condition = ""
|
||||
if self.item_code:
|
||||
@ -134,10 +140,11 @@ class ProductionPlan(Document):
|
||||
self.calculate_total_planned_qty()
|
||||
|
||||
def get_mr_items(self):
|
||||
mr_list = [d.material_request for d in self.material_requests if d.material_request]
|
||||
if not mr_list:
|
||||
msgprint(_("Please enter Material Requests in the above table"))
|
||||
return []
|
||||
# Check for empty table or empty rows
|
||||
if not self.get("material_requests") or not self.get_so_mr_list("material_request", "material_requests"):
|
||||
frappe.throw(_("Please fill the Material Requests table"), title=_("Material Requests Required"))
|
||||
|
||||
mr_list = self.get_so_mr_list("material_request", "material_requests")
|
||||
|
||||
item_condition = ""
|
||||
if self.item_code:
|
||||
@ -628,16 +635,19 @@ def get_items_for_material_requests(doc, warehouses=None):
|
||||
|
||||
if warehouse_list:
|
||||
warehouses = list(set(warehouse_list))
|
||||
|
||||
|
||||
if doc.get("for_warehouse") and doc.get("for_warehouse") in warehouses:
|
||||
warehouses.remove(doc.get("for_warehouse"))
|
||||
|
||||
warehouse_list = None
|
||||
|
||||
doc['mr_items'] = []
|
||||
|
||||
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
|
||||
if not po_items:
|
||||
frappe.throw(_("Items are required to pull the raw materials which is associated with it."))
|
||||
# Check for empty table or empty rows
|
||||
if not po_items or not [row.get('item_code') for row in po_items if row.get('item_code')]:
|
||||
frappe.throw(_("Items to Manufacture are required to pull the Raw Materials associated with it."),
|
||||
title=_("Items Required"))
|
||||
|
||||
company = doc.get('company')
|
||||
ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
|
||||
|
@ -706,3 +706,4 @@ 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
|
||||
erpnext.patches.v12_0.add_taxjar_integration_field
|
||||
|
12
erpnext/patches/v12_0/add_taxjar_integration_field.py
Normal file
12
erpnext/patches/v12_0/add_taxjar_integration_field.py
Normal file
@ -0,0 +1,12 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from erpnext.regional.united_states.setup import make_custom_fields
|
||||
|
||||
|
||||
def execute():
|
||||
company = frappe.get_all('Company', filters={'country': 'United States'})
|
||||
if not company:
|
||||
return
|
||||
|
||||
make_custom_fields()
|
@ -5,6 +5,8 @@ from erpnext.regional.united_states.setup import make_custom_fields
|
||||
def execute():
|
||||
|
||||
frappe.reload_doc('accounts', 'doctype', 'allowed_to_transact_with', force=True)
|
||||
frappe.reload_doc('accounts', 'doctype', 'pricing_rule_detail', force=True)
|
||||
frappe.reload_doc('crm', 'doctype', 'lost_reason_detail', force=True)
|
||||
|
||||
company = frappe.get_all('Company', filters = {'country': 'United States'})
|
||||
if not company:
|
||||
|
@ -381,7 +381,6 @@
|
||||
{
|
||||
"fieldname": "earning",
|
||||
"fieldtype": "Column Break",
|
||||
"label": "Earning",
|
||||
"oldfieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1,
|
||||
@ -400,7 +399,6 @@
|
||||
{
|
||||
"fieldname": "deduction",
|
||||
"fieldtype": "Column Break",
|
||||
"label": "Deduction",
|
||||
"oldfieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1,
|
||||
@ -616,7 +614,7 @@
|
||||
"idx": 9,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-06-22 14:42:43.921828",
|
||||
"modified": "2020-06-25 14:42:43.921828",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Salary Slip",
|
||||
|
@ -271,6 +271,7 @@ 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()
|
||||
|
||||
|
@ -35,7 +35,9 @@ frappe.ui.form.on('Salary Structure', {
|
||||
|
||||
d.show()
|
||||
});
|
||||
frm.get_field("conditions_and_formula_variable_and_example").$wrapper.append(frm.doc.filters_html).append(help_button)
|
||||
let help_button_wrapper = frm.get_field("conditions_and_formula_variable_and_example").$wrapper;
|
||||
help_button_wrapper.empty();
|
||||
help_button_wrapper.append(frm.doc.filters_html).append(help_button)
|
||||
|
||||
frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet)
|
||||
|
||||
|
@ -6,8 +6,8 @@ import frappe, erpnext
|
||||
from frappe import _
|
||||
|
||||
def execute(filters=None):
|
||||
columns = get_columns(filters)
|
||||
data = get_data(filters)
|
||||
columns = get_columns(filters) if len(data) else []
|
||||
|
||||
return columns, data
|
||||
|
||||
@ -78,8 +78,11 @@ def get_conditions(filters):
|
||||
if filters.get("company"):
|
||||
conditions.append("sal.company = '%s' " % (filters["company"]) )
|
||||
|
||||
if filters.get("period"):
|
||||
conditions.append("month(sal.start_date) = '%s' " % (filters["period"]))
|
||||
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)
|
||||
|
||||
@ -96,6 +99,9 @@ def get_data(filters):
|
||||
|
||||
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
|
||||
|
@ -84,9 +84,11 @@ def get_conditions(filters):
|
||||
if filters.get("company"):
|
||||
conditions.append("company = '%s' " % (filters["company"]) )
|
||||
|
||||
if filters.get("period"):
|
||||
conditions.append("month(start_date) = '%s' " % (filters["period"]))
|
||||
conditions.append("year(start_date) = '%s' " % (frappe.utils.getdate().year))
|
||||
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)
|
||||
|
||||
|
@ -239,13 +239,12 @@ def get_next_attribute_and_values(item_code, selected_attributes):
|
||||
if exact_match:
|
||||
data = get_product_info_for_website(exact_match[0])
|
||||
product_info = data.product_info
|
||||
product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock)
|
||||
if not data.cart_settings.show_price:
|
||||
product_info = None
|
||||
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,
|
||||
|
@ -140,52 +140,6 @@ class TestTimesheet(unittest.TestCase):
|
||||
settings.ignore_employee_time_overlap = initial_setting
|
||||
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 = emp
|
||||
timesheet.company = '_Test Company'
|
||||
timesheet.append(
|
||||
'time_logs',
|
||||
{
|
||||
"activity_type": "_Test Activity Type",
|
||||
"from_time": now_datetime(),
|
||||
"to_time": now_datetime() + datetime.timedelta(days= 4)
|
||||
}
|
||||
)
|
||||
timesheet.save()
|
||||
|
||||
ts = frappe.get_doc('Timesheet', timesheet.name)
|
||||
self.assertEqual(ts.total_hours, 32)
|
||||
ts.submit()
|
||||
ts.cancel()
|
||||
|
||||
company = frappe.get_doc('Company', "_Test Company")
|
||||
company.standard_working_hours = 0
|
||||
company.save()
|
||||
|
||||
timesheet = frappe.new_doc("Timesheet")
|
||||
timesheet.employee = emp
|
||||
timesheet.company = '_Test Company'
|
||||
timesheet.append(
|
||||
'time_logs',
|
||||
{
|
||||
"activity_type": "_Test Activity Type",
|
||||
"from_time": now_datetime(),
|
||||
"to_time": now_datetime() + datetime.timedelta(days= 4)
|
||||
}
|
||||
)
|
||||
timesheet.save()
|
||||
|
||||
ts = frappe.get_doc('Timesheet', timesheet.name)
|
||||
self.assertEqual(ts.total_hours, 96)
|
||||
ts.submit()
|
||||
ts.cancel()
|
||||
|
||||
def make_salary_structure_for_timesheet(employee):
|
||||
salary_structure_name = "Timesheet Salary Structure Test"
|
||||
|
@ -162,19 +162,11 @@ frappe.ui.form.on("Timesheet Detail", {
|
||||
|
||||
to_time: function(frm, cdt, cdn) {
|
||||
var child = locals[cdt][cdn];
|
||||
var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / ( 60 * 60 * 24);
|
||||
var std_working_hours = 0;
|
||||
|
||||
if(frm._setting_hours) return;
|
||||
|
||||
var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600;
|
||||
std_working_hours = time_diff * frappe.working_hours;
|
||||
|
||||
if (std_working_hours < hours && std_working_hours > 0) {
|
||||
frappe.model.set_value(cdt, cdn, "hours", std_working_hours);
|
||||
} else {
|
||||
frappe.model.set_value(cdt, cdn, "hours", hours);
|
||||
}
|
||||
frappe.model.set_value(cdt, cdn, "hours", hours);
|
||||
},
|
||||
|
||||
time_logs_add: function(frm) {
|
||||
@ -236,23 +228,12 @@ var calculate_end_time = function(frm, cdt, cdn) {
|
||||
|
||||
let d = moment(child.from_time);
|
||||
if(child.hours) {
|
||||
var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / (60 * 60 * 24);
|
||||
var std_working_hours = 0;
|
||||
var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600;
|
||||
|
||||
std_working_hours = time_diff * frappe.working_hours;
|
||||
|
||||
if (std_working_hours < hours && std_working_hours > 0) {
|
||||
frappe.model.set_value(cdt, cdn, "hours", std_working_hours);
|
||||
frappe.model.set_value(cdt, cdn, "to_time", d.add(hours, "hours").format(frappe.defaultDatetimeFormat));
|
||||
} else {
|
||||
d.add(child.hours, "hours");
|
||||
frm._setting_hours = true;
|
||||
frappe.model.set_value(cdt, cdn, "to_time",
|
||||
d.format(frappe.defaultDatetimeFormat)).then(() => {
|
||||
frm._setting_hours = false;
|
||||
});
|
||||
}
|
||||
d.add(child.hours, "hours");
|
||||
frm._setting_hours = true;
|
||||
frappe.model.set_value(cdt, cdn, "to_time",
|
||||
d.format(frappe.defaultDatetimeFormat)).then(() => {
|
||||
frm._setting_hours = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,6 @@ class Timesheet(Document):
|
||||
self.set_status()
|
||||
self.validate_dates()
|
||||
self.validate_time_logs()
|
||||
self.calculate_std_hours()
|
||||
self.update_cost()
|
||||
self.calculate_total_amounts()
|
||||
self.calculate_percentage_billed()
|
||||
@ -91,17 +90,6 @@ class Timesheet(Document):
|
||||
self.start_date = getdate(start_date)
|
||||
self.end_date = getdate(end_date)
|
||||
|
||||
def calculate_std_hours(self):
|
||||
std_working_hours = frappe.get_value("Company", self.company, 'standard_working_hours')
|
||||
|
||||
for time in self.time_logs:
|
||||
if time.from_time and time.to_time:
|
||||
if flt(std_working_hours) and date_diff(time.to_time, time.from_time):
|
||||
time.hours = flt(std_working_hours) * date_diff(time.to_time, time.from_time)
|
||||
else:
|
||||
if not time.hours:
|
||||
time.hours = time_diff_in_hours(time.to_time, time.from_time)
|
||||
|
||||
def before_cancel(self):
|
||||
self.set_status()
|
||||
|
||||
|
@ -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"));
|
||||
|
@ -159,6 +159,26 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
};
|
||||
});
|
||||
}
|
||||
if (this.frm.fields_dict["items"].grid.get_field("cost_center")) {
|
||||
this.frm.set_query("cost_center", "items", function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
"company": doc.company,
|
||||
"is_group": 0
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (this.frm.fields_dict["items"].grid.get_field("expense_account")) {
|
||||
this.frm.set_query("expense_account", "items", function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
"company": doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if(frappe.meta.get_docfield(this.frm.doc.doctype, "pricing_rules")) {
|
||||
this.frm.set_indicator_formatter('pricing_rule', function(doc) {
|
||||
|
@ -11,8 +11,8 @@ erpnext.salary_slip_deductions_report_filters = {
|
||||
default: frappe.defaults.get_user_default("Company"),
|
||||
},
|
||||
{
|
||||
fieldname: "period",
|
||||
label: __("Period"),
|
||||
fieldname: "month",
|
||||
label: __("Month"),
|
||||
fieldtype: "Select",
|
||||
reqd: 1 ,
|
||||
options: [
|
||||
@ -31,6 +31,12 @@ erpnext.salary_slip_deductions_report_filters = {
|
||||
],
|
||||
default: frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1
|
||||
},
|
||||
{
|
||||
fieldname:"year",
|
||||
label: __("Year"),
|
||||
fieldtype: "Select",
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname: "department",
|
||||
label: __("Department"),
|
||||
@ -43,5 +49,18 @@ erpnext.salary_slip_deductions_report_filters = {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -7,8 +7,8 @@ from frappe import _
|
||||
from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions
|
||||
|
||||
def execute(filters=None):
|
||||
columns = get_columns(filters)
|
||||
data = get_data(filters)
|
||||
columns = get_columns(filters) if len(data) else []
|
||||
|
||||
return columns, data
|
||||
|
||||
@ -45,6 +45,9 @@ def get_data(filters):
|
||||
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
|
||||
|
@ -3,11 +3,12 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import getdate
|
||||
from frappe import _
|
||||
|
||||
def execute(filters=None):
|
||||
columns = get_columns(filters)
|
||||
data = get_data(filters)
|
||||
columns = get_columns(filters) if len(data) else []
|
||||
|
||||
return columns, data
|
||||
|
||||
@ -71,10 +72,13 @@ def get_conditions(filters):
|
||||
conditions.append("sal.branch = '%s' " % (filters["branch"]) )
|
||||
|
||||
if filters.get("company"):
|
||||
conditions.append("sal.company = '%s' " % (filters["company"]) )
|
||||
conditions.append("sal.company = '%s' " % (filters["company"]))
|
||||
|
||||
if filters.get("period"):
|
||||
conditions.append("month(sal.start_date) = '%s' " % (filters["period"]))
|
||||
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"]))
|
||||
@ -114,6 +118,9 @@ def get_data(filters):
|
||||
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
|
||||
@ -150,4 +157,12 @@ def get_data(filters):
|
||||
|
||||
data.append(employee)
|
||||
|
||||
return data
|
||||
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)
|
@ -14,6 +14,22 @@ def make_custom_fields(update=True):
|
||||
'Supplier': [
|
||||
dict(fieldname='irs_1099', fieldtype='Check', insert_after='tax_id',
|
||||
label='Is IRS 1099 reporting required for supplier?')
|
||||
],
|
||||
'Sales Order': [
|
||||
dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_and_charges',
|
||||
label='Is customer exempted from sales tax?')
|
||||
],
|
||||
'Sales Invoice': [
|
||||
dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_section',
|
||||
label='Is customer exempted from sales tax?')
|
||||
],
|
||||
'Customer': [
|
||||
dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='represents_company',
|
||||
label='Is customer exempted from sales tax?')
|
||||
],
|
||||
'Quotation': [
|
||||
dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_and_charges',
|
||||
label='Is customer exempted from sales tax?')
|
||||
]
|
||||
}
|
||||
create_custom_fields(custom_fields, update=update)
|
||||
|
@ -388,8 +388,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
|
||||
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_list = [user for user in credit_controller_users if frappe.db.exists("Employee", {"prefered_email": user})]
|
||||
credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users_list]
|
||||
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)))
|
||||
@ -409,7 +408,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
|
||||
'customer': customer,
|
||||
'customer_outstanding': customer_outstanding,
|
||||
'credit_limit': credit_limit,
|
||||
'credit_controller_users_list': credit_controller_users_list
|
||||
'credit_controller_users_list': credit_controller_users
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -280,5 +280,3 @@ def make_quotation(**args):
|
||||
qo.submit()
|
||||
|
||||
return qo
|
||||
|
||||
|
||||
|
@ -158,7 +158,7 @@ def get_data():
|
||||
}
|
||||
pending_so.append(so_record)
|
||||
else:
|
||||
for item in bundled_item_map.get((so.name, so.item_code)):
|
||||
for item in bundled_item_map.get((so.name, so.item_code), []):
|
||||
material_requests_against_so = materials_request_dict.get((so.name, item.item_code)) or {}
|
||||
if flt(item.qty) > flt(material_requests_against_so.get('qty')):
|
||||
so_record = {
|
||||
|
@ -22,7 +22,6 @@
|
||||
"default_letter_head",
|
||||
"default_holiday_list",
|
||||
"default_finance_book",
|
||||
"standard_working_hours",
|
||||
"default_selling_terms",
|
||||
"default_buying_terms",
|
||||
"default_warehouse_for_sales_return",
|
||||
@ -240,11 +239,6 @@
|
||||
"label": "Default Holiday List",
|
||||
"options": "Holiday List"
|
||||
},
|
||||
{
|
||||
"fieldname": "standard_working_hours",
|
||||
"fieldtype": "Float",
|
||||
"label": "Standard Working Hours"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_warehouse_for_sales_return",
|
||||
"fieldtype": "Link",
|
||||
@ -746,7 +740,7 @@
|
||||
"image_field": "company_logo",
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2020-06-20 11:38:43.178970",
|
||||
"modified": "2020-06-24 12:45:31.462195",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
|
@ -405,8 +405,8 @@ class EmailDigest(Document):
|
||||
|
||||
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
|
||||
count(*) from `tabSales Order`
|
||||
where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed"
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
|
||||
where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed" and company = %(company)s
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
|
||||
|
||||
label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_bill"),
|
||||
report_type="Report Builder",
|
||||
@ -430,8 +430,8 @@ class EmailDigest(Document):
|
||||
|
||||
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_delivered/100)),0),
|
||||
count(*) from `tabSales Order`
|
||||
where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered"
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
|
||||
where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered" and company = %(company)s
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
|
||||
|
||||
label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_deliver"),
|
||||
report_type="Report Builder",
|
||||
@ -455,8 +455,8 @@ class EmailDigest(Document):
|
||||
|
||||
value, count = frappe.db.sql("""select ifnull((sum(grand_total))-(sum(grand_total*per_received/100)),0),
|
||||
count(*) from `tabPurchase Order`
|
||||
where (transaction_date <= %(to_date)s) and per_received < 100
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
|
||||
where (transaction_date <= %(to_date)s) and per_received < 100 and company = %(company)s
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
|
||||
|
||||
label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_receive"),
|
||||
report_type="Report Builder",
|
||||
@ -480,8 +480,8 @@ class EmailDigest(Document):
|
||||
|
||||
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
|
||||
count(*) from `tabPurchase Order`
|
||||
where (transaction_date <= %(to_date)s) and per_billed < 100
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
|
||||
where (transaction_date <= %(to_date)s) and per_billed < 100 and company = %(company)s
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
|
||||
|
||||
label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_bill"),
|
||||
report_type="Report Builder",
|
||||
|
@ -13,6 +13,12 @@ $.extend(cur_frm.cscript, {
|
||||
},
|
||||
enable_checkout: function(){
|
||||
toggle_mandatory(cur_frm)
|
||||
},
|
||||
enabled: function() {
|
||||
if (cur_frm.doc.enabled === 1) {
|
||||
cur_frm.doc.show_configure_button = 1;
|
||||
cur_frm.refresh_field('show_configure_button');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -55,7 +55,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False):
|
||||
|
||||
def set_product_info_for_website(item):
|
||||
"""set product price uom for website"""
|
||||
product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True)
|
||||
product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get("product_info")
|
||||
|
||||
if product_info:
|
||||
item.update(product_info)
|
||||
|
@ -71,7 +71,7 @@
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
@ -88,7 +88,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-09-12 09:30:03.951743",
|
||||
"modified": "2020-06-26 09:30:03.951743",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Customs Tariff Number",
|
||||
@ -143,4 +143,4 @@
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:serial_no",
|
||||
"creation": "2013-05-16 10:59:15",
|
||||
"description": "Distinct unit of an Item",
|
||||
@ -427,7 +426,7 @@
|
||||
"icon": "fa fa-barcode",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-21 19:29:58.517772",
|
||||
"modified": "2020-06-25 15:53:50.900855",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Serial No",
|
||||
|
@ -79,7 +79,7 @@ frappe.ui.form.on("Issue", {
|
||||
method: "erpnext.support.doctype.issue.issue.make_task",
|
||||
frm: frm
|
||||
});
|
||||
}, __("Make"));
|
||||
}, __("Create"));
|
||||
|
||||
} else {
|
||||
if (frm.doc.service_level_agreement) {
|
||||
@ -232,4 +232,4 @@ function get_status(variance) {
|
||||
} else {
|
||||
return {"diff_display": "Failed", "indicator": "red"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,47 +79,52 @@ class Issue(Document):
|
||||
|
||||
def handle_hold_time(self, status):
|
||||
if self.service_level_agreement:
|
||||
# set response and resolution variance as None as the issue is on Hold for status as Replied
|
||||
# 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 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
|
||||
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 Replied to any other 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()
|
||||
update_values['total_hold_time'] = hold_time + time_diff_in_seconds(now_time, self.on_hold_since)
|
||||
# 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 Replied to Open
|
||||
# add hold time to SLA variables
|
||||
if self.status == "Open" and status in hold_statuses:
|
||||
start_date_time = get_datetime(self.service_level_agreement_creation)
|
||||
priority = get_priority(self)
|
||||
now_time = frappe.flags.current_time or now_datetime()
|
||||
hold_time = time_diff_in_seconds(now_time, self.on_hold_since)
|
||||
# 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)
|
||||
update_values['response_by'] = add_to_date(response_by, seconds=round(hold_time))
|
||||
response_by_variance = round(time_diff_in_hours(self.response_by, now_time))
|
||||
update_values['response_by_variance'] = response_by_variance + (hold_time // 3600)
|
||||
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)
|
||||
update_values['resolution_by'] = add_to_date(resolution_by, seconds=round(hold_time))
|
||||
resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time))
|
||||
update_values['resolution_by_variance'] = resolution_by_variance + (hold_time // 3600)
|
||||
update_values['on_hold_since'] = None
|
||||
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)
|
||||
self.db_set(update_values)
|
||||
|
||||
def update_agreement_status(self):
|
||||
if self.service_level_agreement and self.agreement_fulfilled == "Ongoing":
|
||||
|
@ -4,8 +4,10 @@ gocardless-pro==1.11.0
|
||||
googlemaps==3.1.1
|
||||
pandas==0.24.2
|
||||
plaid-python==3.4.0
|
||||
pycountry==19.8.18
|
||||
PyGithub==1.44.1
|
||||
python-stdnum==1.12
|
||||
taxjar==1.9.0
|
||||
tweepy==3.8.0
|
||||
Unidecode==1.1.1
|
||||
WooCommerce==2.1.1
|
||||
tweepy==3.8.0
|
Loading…
x
Reference in New Issue
Block a user