mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-27 08:54:45 +00:00
Merge branch 'version-13-hotfix' into e-commerce-refactor
This commit is contained in:
@@ -13,3 +13,4 @@
|
|||||||
|
|
||||||
# Whitespace trimming throughout codebase
|
# Whitespace trimming throughout codebase
|
||||||
9bb69e711a5da43aaf8c8ecb5601aeffd89dbe5a
|
9bb69e711a5da43aaf8c8ecb5601aeffd89dbe5a
|
||||||
|
f0bcb753fb7ebbb64bb0d6906d431d002f0f7d8f
|
||||||
|
|||||||
72
.github/helper/.flake8_strict
vendored
Normal file
72
.github/helper/.flake8_strict
vendored
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
[flake8]
|
||||||
|
ignore =
|
||||||
|
B007,
|
||||||
|
B950,
|
||||||
|
E101,
|
||||||
|
E111,
|
||||||
|
E114,
|
||||||
|
E116,
|
||||||
|
E117,
|
||||||
|
E121,
|
||||||
|
E122,
|
||||||
|
E123,
|
||||||
|
E124,
|
||||||
|
E125,
|
||||||
|
E126,
|
||||||
|
E127,
|
||||||
|
E128,
|
||||||
|
E131,
|
||||||
|
E201,
|
||||||
|
E202,
|
||||||
|
E203,
|
||||||
|
E211,
|
||||||
|
E221,
|
||||||
|
E222,
|
||||||
|
E223,
|
||||||
|
E224,
|
||||||
|
E225,
|
||||||
|
E226,
|
||||||
|
E228,
|
||||||
|
E231,
|
||||||
|
E241,
|
||||||
|
E242,
|
||||||
|
E251,
|
||||||
|
E261,
|
||||||
|
E262,
|
||||||
|
E265,
|
||||||
|
E266,
|
||||||
|
E271,
|
||||||
|
E272,
|
||||||
|
E273,
|
||||||
|
E274,
|
||||||
|
E301,
|
||||||
|
E302,
|
||||||
|
E303,
|
||||||
|
E305,
|
||||||
|
E306,
|
||||||
|
E401,
|
||||||
|
E402,
|
||||||
|
E501,
|
||||||
|
E502,
|
||||||
|
E701,
|
||||||
|
E702,
|
||||||
|
E703,
|
||||||
|
E741,
|
||||||
|
F401,
|
||||||
|
F403,
|
||||||
|
W191,
|
||||||
|
W291,
|
||||||
|
W292,
|
||||||
|
W293,
|
||||||
|
W391,
|
||||||
|
W503,
|
||||||
|
W504,
|
||||||
|
E711,
|
||||||
|
E129,
|
||||||
|
F841,
|
||||||
|
E713,
|
||||||
|
E712,
|
||||||
|
|
||||||
|
|
||||||
|
max-line-length = 200
|
||||||
|
exclude=.github/helper/semgrep_rules,test_*.py
|
||||||
15
.github/helper/semgrep_rules/report.py
vendored
Normal file
15
.github/helper/semgrep_rules/report.py
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from frappe import _
|
||||||
|
|
||||||
|
|
||||||
|
# ruleid: frappe-missing-translate-function-in-report-python
|
||||||
|
{"label": "Field Label"}
|
||||||
|
|
||||||
|
# ruleid: frappe-missing-translate-function-in-report-python
|
||||||
|
dict(label="Field Label")
|
||||||
|
|
||||||
|
|
||||||
|
# ok: frappe-missing-translate-function-in-report-python
|
||||||
|
{"label": _("Field Label")}
|
||||||
|
|
||||||
|
# ok: frappe-missing-translate-function-in-report-python
|
||||||
|
dict(label=_("Field Label"))
|
||||||
21
.github/helper/semgrep_rules/report.yml
vendored
Normal file
21
.github/helper/semgrep_rules/report.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
rules:
|
||||||
|
- id: frappe-missing-translate-function-in-report-python
|
||||||
|
paths:
|
||||||
|
include:
|
||||||
|
- "**/report"
|
||||||
|
exclude:
|
||||||
|
- "**/regional"
|
||||||
|
pattern-either:
|
||||||
|
- patterns:
|
||||||
|
- pattern: |
|
||||||
|
{..., "label": "...", ...}
|
||||||
|
- pattern-not: |
|
||||||
|
{..., "label": _("..."), ...}
|
||||||
|
- patterns:
|
||||||
|
- pattern: dict(..., label="...", ...)
|
||||||
|
- pattern-not: dict(..., label=_("..."), ...)
|
||||||
|
message: |
|
||||||
|
All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations
|
||||||
|
languages: [python]
|
||||||
|
severity: ERROR
|
||||||
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
name: Semgrep
|
name: Linters
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request: { }
|
pull_request: { }
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
semgrep:
|
|
||||||
name: Frappe Linter
|
linters:
|
||||||
|
name: linters
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -16,3 +17,11 @@ jobs:
|
|||||||
config: >-
|
config: >-
|
||||||
r/python.lang.correctness
|
r/python.lang.correctness
|
||||||
.github/helper/semgrep_rules
|
.github/helper/semgrep_rules
|
||||||
|
|
||||||
|
- name: Set up Python 3.8
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
|
||||||
|
- name: Install and Run Pre-commit
|
||||||
|
uses: pre-commit/action@v2.0.0
|
||||||
29
.pre-commit-config.yaml
Normal file
29
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
exclude: 'node_modules|.git'
|
||||||
|
default_stages: [commit]
|
||||||
|
fail_fast: false
|
||||||
|
|
||||||
|
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.0.1
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
files: "erpnext.*"
|
||||||
|
exclude: ".*json$|.*txt$|.*csv|.*md"
|
||||||
|
- id: check-yaml
|
||||||
|
- id: no-commit-to-branch
|
||||||
|
args: ['--branch', 'develop']
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- id: check-ast
|
||||||
|
|
||||||
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
|
rev: 3.9.2
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
args: ['--config', '.github/helper/.flake8_strict']
|
||||||
|
exclude: ".*setup.py$"
|
||||||
|
|
||||||
|
ci:
|
||||||
|
autoupdate_schedule: weekly
|
||||||
|
skip: []
|
||||||
|
submodules: false
|
||||||
@@ -2,7 +2,12 @@ context('Organizational Chart', () => {
|
|||||||
before(() => {
|
before(() => {
|
||||||
cy.login();
|
cy.login();
|
||||||
cy.visit('/app/website');
|
cy.visit('/app/website');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('navigates to org chart', () => {
|
||||||
|
cy.visit('/app');
|
||||||
cy.awesomebar('Organizational Chart');
|
cy.awesomebar('Organizational Chart');
|
||||||
|
cy.url().should('include', '/organizational-chart');
|
||||||
|
|
||||||
cy.window().its('frappe.csrf_token').then(csrf_token => {
|
cy.window().its('frappe.csrf_token').then(csrf_token => {
|
||||||
return cy.request({
|
return cy.request({
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
context('Organizational Chart Mobile', () => {
|
context('Organizational Chart Mobile', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.login();
|
cy.login();
|
||||||
cy.viewport(375, 667);
|
|
||||||
cy.visit('/app/website');
|
cy.visit('/app/website');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('navigates to org chart', () => {
|
||||||
|
cy.viewport(375, 667);
|
||||||
|
cy.visit('/app');
|
||||||
cy.awesomebar('Organizational Chart');
|
cy.awesomebar('Organizational Chart');
|
||||||
|
cy.url().should('include', '/organizational-chart');
|
||||||
|
|
||||||
cy.window().its('frappe.csrf_token').then(csrf_token => {
|
cy.window().its('frappe.csrf_token').then(csrf_token => {
|
||||||
return cy.request({
|
return cy.request({
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class ERPNextAddress(Address):
|
|||||||
customers = frappe.db.get_all("Customer", filters=filters, as_list=True)
|
customers = frappe.db.get_all("Customer", filters=filters, as_list=True)
|
||||||
for customer_name in customers:
|
for customer_name in customers:
|
||||||
frappe.db.set_value("Customer", customer_name[0], "primary_address", address_display)
|
frappe.db.set_value("Customer", customer_name[0], "primary_address", address_display)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_shipping_address(company, address = None):
|
def get_shipping_address(company, address = None):
|
||||||
filters = [
|
filters = [
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
|||||||
try:
|
try:
|
||||||
make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
|
make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except:
|
except Exception:
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
traceback = frappe.get_traceback()
|
traceback = frappe.get_traceback()
|
||||||
frappe.log_error(message=traceback)
|
frappe.log_error(message=traceback)
|
||||||
@@ -430,7 +430,7 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
|
|||||||
|
|
||||||
if submit:
|
if submit:
|
||||||
journal_entry.submit()
|
journal_entry.submit()
|
||||||
except:
|
except Exception:
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
traceback = frappe.get_traceback()
|
traceback = frappe.get_traceback()
|
||||||
frappe.log_error(message=traceback)
|
frappe.log_error(message=traceback)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class BankTransaction(StatusUpdater):
|
|||||||
self.update_allocations()
|
self.update_allocations()
|
||||||
self.clear_linked_payment_entries()
|
self.clear_linked_payment_entries()
|
||||||
self.set_status(update=True)
|
self.set_status(update=True)
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.clear_linked_payment_entries(for_cancel=True)
|
self.clear_linked_payment_entries(for_cancel=True)
|
||||||
self.set_status(update=True)
|
self.set_status(update=True)
|
||||||
@@ -45,7 +45,7 @@ class BankTransaction(StatusUpdater):
|
|||||||
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
|
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
|
||||||
|
|
||||||
self.reload()
|
self.reload()
|
||||||
|
|
||||||
def clear_linked_payment_entries(self, for_cancel=False):
|
def clear_linked_payment_entries(self, for_cancel=False):
|
||||||
for payment_entry in self.payment_entries:
|
for payment_entry in self.payment_entries:
|
||||||
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
|
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
|
||||||
@@ -77,7 +77,7 @@ class BankTransaction(StatusUpdater):
|
|||||||
|
|
||||||
def get_reconciled_bank_transactions(payment_entry):
|
def get_reconciled_bank_transactions(payment_entry):
|
||||||
reconciled_bank_transactions = frappe.get_all(
|
reconciled_bank_transactions = frappe.get_all(
|
||||||
'Bank Transaction Payments',
|
'Bank Transaction Payments',
|
||||||
filters = {
|
filters = {
|
||||||
'payment_entry': payment_entry.payment_entry
|
'payment_entry': payment_entry.payment_entry
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ class PartyLink(Document):
|
|||||||
if self.primary_role not in ['Customer', 'Supplier']:
|
if self.primary_role not in ['Customer', 'Supplier']:
|
||||||
frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."),
|
frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."),
|
||||||
title=_("Invalid Primary Role"))
|
title=_("Invalid Primary Role"))
|
||||||
|
|
||||||
existing_party_link = frappe.get_all('Party Link', {
|
existing_party_link = frappe.get_all('Party Link', {
|
||||||
'primary_party': self.secondary_party
|
'primary_party': self.secondary_party
|
||||||
}, pluck="primary_role")
|
}, pluck="primary_role")
|
||||||
if existing_party_link:
|
if existing_party_link:
|
||||||
frappe.throw(_('{} {} is already linked with another {}')
|
frappe.throw(_('{} {} is already linked with another {}')
|
||||||
.format(self.secondary_role, self.secondary_party, existing_party_link[0]))
|
.format(self.secondary_role, self.secondary_party, existing_party_link[0]))
|
||||||
|
|
||||||
existing_party_link = frappe.get_all('Party Link', {
|
existing_party_link = frappe.get_all('Party Link', {
|
||||||
'secondary_party': self.primary_party
|
'secondary_party': self.primary_party
|
||||||
}, pluck="primary_role")
|
}, pluck="primary_role")
|
||||||
|
|||||||
@@ -872,7 +872,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
|
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
|
||||||
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
|
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
|
||||||
unallocated_amount = (frm.doc.base_received_amount + total_deductions + frm.doc.base_total_taxes_and_charges
|
unallocated_amount = (frm.doc.base_received_amount + total_deductions + frm.doc.base_total_taxes_and_charges
|
||||||
+ frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
|
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
|
||||||
} else if (frm.doc.payment_type == "Pay"
|
} else if (frm.doc.payment_type == "Pay"
|
||||||
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
|
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
|
||||||
&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
|
&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
|
||||||
|
|||||||
@@ -484,7 +484,7 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
def validate_amounts(self):
|
def validate_amounts(self):
|
||||||
self.validate_received_amount()
|
self.validate_received_amount()
|
||||||
|
|
||||||
def validate_received_amount(self):
|
def validate_received_amount(self):
|
||||||
if self.paid_from_account_currency == self.paid_to_account_currency:
|
if self.paid_from_account_currency == self.paid_to_account_currency:
|
||||||
if self.paid_amount != self.received_amount:
|
if self.paid_amount != self.received_amount:
|
||||||
|
|||||||
@@ -2,46 +2,10 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.provide("erpnext.accounts");
|
frappe.provide("erpnext.accounts");
|
||||||
|
|
||||||
frappe.ui.form.on("Payment Reconciliation Payment", {
|
|
||||||
invoice_number: function(frm, cdt, cdn) {
|
|
||||||
var row = locals[cdt][cdn];
|
|
||||||
if(row.invoice_number) {
|
|
||||||
var parts = row.invoice_number.split(' | ');
|
|
||||||
var invoice_type = parts[0];
|
|
||||||
var invoice_number = parts[1];
|
|
||||||
|
|
||||||
var invoice_amount = frm.doc.invoices.filter(function(d) {
|
|
||||||
return d.invoice_type === invoice_type && d.invoice_number === invoice_number;
|
|
||||||
})[0].outstanding_amount;
|
|
||||||
|
|
||||||
frappe.model.set_value(cdt, cdn, "allocated_amount", Math.min(invoice_amount, row.amount));
|
|
||||||
|
|
||||||
frm.call({
|
|
||||||
doc: frm.doc,
|
|
||||||
method: 'get_difference_amount',
|
|
||||||
args: {
|
|
||||||
child_row: row
|
|
||||||
},
|
|
||||||
callback: function(r, rt) {
|
|
||||||
if(r.message) {
|
|
||||||
frappe.model.set_value(cdt, cdn,
|
|
||||||
"difference_amount", r.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
|
erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
|
||||||
onload: function() {
|
onload: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
this.frm.set_query("party", function() {
|
|
||||||
check_mandatory(me.frm);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.frm.set_query("party_type", function() {
|
this.frm.set_query("party_type", function() {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
"filters": {
|
||||||
@@ -88,15 +52,36 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
|
|
||||||
refresh: function() {
|
refresh: function() {
|
||||||
this.frm.disable_save();
|
this.frm.disable_save();
|
||||||
this.toggle_primary_action();
|
|
||||||
|
if (this.frm.doc.receivable_payable_account) {
|
||||||
|
this.frm.add_custom_button(__('Get Unreconciled Entries'), () =>
|
||||||
|
this.frm.trigger("get_unreconciled_entries")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (this.frm.doc.invoices.length && this.frm.doc.payments.length) {
|
||||||
|
this.frm.add_custom_button(__('Allocate'), () =>
|
||||||
|
this.frm.trigger("allocate")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (this.frm.doc.allocation.length) {
|
||||||
|
this.frm.add_custom_button(__('Reconcile'), () =>
|
||||||
|
this.frm.trigger("reconcile")
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onload_post_render: function() {
|
company: function() {
|
||||||
this.toggle_primary_action();
|
var me = this;
|
||||||
|
this.frm.set_value('receivable_payable_account', '');
|
||||||
|
me.frm.clear_table("allocation");
|
||||||
|
me.frm.clear_table("invoices");
|
||||||
|
me.frm.clear_table("payments");
|
||||||
|
me.frm.refresh_fields();
|
||||||
|
me.frm.trigger('party');
|
||||||
},
|
},
|
||||||
|
|
||||||
party: function() {
|
party: function() {
|
||||||
var me = this
|
var me = this;
|
||||||
if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
|
if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: "erpnext.accounts.party.get_party_account",
|
method: "erpnext.accounts.party.get_party_account",
|
||||||
@@ -109,6 +94,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
if (!r.exc && r.message) {
|
if (!r.exc && r.message) {
|
||||||
me.frm.set_value("receivable_payable_account", r.message);
|
me.frm.set_value("receivable_payable_account", r.message);
|
||||||
}
|
}
|
||||||
|
me.frm.refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -120,16 +106,41 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
doc: me.frm.doc,
|
doc: me.frm.doc,
|
||||||
method: 'get_unreconciled_entries',
|
method: 'get_unreconciled_entries',
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
me.set_invoice_options();
|
if (!(me.frm.doc.payments.length || me.frm.doc.invoices.length)) {
|
||||||
me.toggle_primary_action();
|
frappe.throw({message: __("No invoice and payment records found for this party")});
|
||||||
|
}
|
||||||
|
me.frm.refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
allocate: function() {
|
||||||
|
var me = this;
|
||||||
|
let payments = me.frm.fields_dict.payments.grid.get_selected_children();
|
||||||
|
if (!(payments.length)) {
|
||||||
|
payments = me.frm.doc.payments;
|
||||||
|
}
|
||||||
|
let invoices = me.frm.fields_dict.invoices.grid.get_selected_children();
|
||||||
|
if (!(invoices.length)) {
|
||||||
|
invoices = me.frm.doc.invoices;
|
||||||
|
}
|
||||||
|
return me.frm.call({
|
||||||
|
doc: me.frm.doc,
|
||||||
|
method: 'allocate_entries',
|
||||||
|
args: {
|
||||||
|
payments: payments,
|
||||||
|
invoices: invoices
|
||||||
|
},
|
||||||
|
callback: function() {
|
||||||
|
me.frm.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
reconcile: function() {
|
reconcile: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
var show_dialog = me.frm.doc.payments.filter(d => d.difference_amount && !d.difference_account);
|
var show_dialog = me.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account);
|
||||||
|
|
||||||
if (show_dialog && show_dialog.length) {
|
if (show_dialog && show_dialog.length) {
|
||||||
|
|
||||||
@@ -138,7 +149,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
title: __("Select Difference Account"),
|
title: __("Select Difference Account"),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
fieldname: "payments", fieldtype: "Table", label: __("Payments"),
|
fieldname: "allocation", fieldtype: "Table", label: __("Allocation"),
|
||||||
data: this.data, in_place_edit: true,
|
data: this.data, in_place_edit: true,
|
||||||
get_data: () => {
|
get_data: () => {
|
||||||
return this.data;
|
return this.data;
|
||||||
@@ -179,10 +190,10 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function() {
|
||||||
const args = dialog.get_values()["payments"];
|
const args = dialog.get_values()["allocation"];
|
||||||
|
|
||||||
args.forEach(d => {
|
args.forEach(d => {
|
||||||
frappe.model.set_value("Payment Reconciliation Payment", d.docname,
|
frappe.model.set_value("Payment Reconciliation Allocation", d.docname,
|
||||||
"difference_account", d.difference_account);
|
"difference_account", d.difference_account);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,9 +203,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
primary_action_label: __('Reconcile Entries')
|
primary_action_label: __('Reconcile Entries')
|
||||||
});
|
});
|
||||||
|
|
||||||
this.frm.doc.payments.forEach(d => {
|
this.frm.doc.allocation.forEach(d => {
|
||||||
if (d.difference_amount && !d.difference_account) {
|
if (d.difference_amount && !d.difference_account) {
|
||||||
dialog.fields_dict.payments.df.data.push({
|
dialog.fields_dict.allocation.df.data.push({
|
||||||
'docname': d.name,
|
'docname': d.name,
|
||||||
'reference_name': d.reference_name,
|
'reference_name': d.reference_name,
|
||||||
'difference_amount': d.difference_amount,
|
'difference_amount': d.difference_amount,
|
||||||
@@ -203,8 +214,8 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.data = dialog.fields_dict.payments.df.data;
|
this.data = dialog.fields_dict.allocation.df.data;
|
||||||
dialog.fields_dict.payments.grid.refresh();
|
dialog.fields_dict.allocation.grid.refresh();
|
||||||
dialog.show();
|
dialog.show();
|
||||||
} else {
|
} else {
|
||||||
this.reconcile_payment_entries();
|
this.reconcile_payment_entries();
|
||||||
@@ -218,48 +229,12 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
doc: me.frm.doc,
|
doc: me.frm.doc,
|
||||||
method: 'reconcile',
|
method: 'reconcile',
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
me.set_invoice_options();
|
me.frm.clear_table("allocation");
|
||||||
me.toggle_primary_action();
|
me.frm.refresh_fields();
|
||||||
|
me.frm.refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
set_invoice_options: function() {
|
|
||||||
var me = this;
|
|
||||||
var invoices = [];
|
|
||||||
|
|
||||||
$.each(me.frm.doc.invoices || [], function(i, row) {
|
|
||||||
if (row.invoice_number && !in_list(invoices, row.invoice_number))
|
|
||||||
invoices.push(row.invoice_type + " | " + row.invoice_number);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (invoices) {
|
|
||||||
this.frm.fields_dict.payments.grid.update_docfield_property(
|
|
||||||
'invoice_number', 'options', "\n" + invoices.join("\n")
|
|
||||||
);
|
|
||||||
|
|
||||||
$.each(me.frm.doc.payments || [], function(i, p) {
|
|
||||||
if(!in_list(invoices, cstr(p.invoice_number))) p.invoice_number = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_field("payments");
|
|
||||||
},
|
|
||||||
|
|
||||||
toggle_primary_action: function() {
|
|
||||||
if ((this.frm.doc.payments || []).length) {
|
|
||||||
this.frm.fields_dict.reconcile.$input
|
|
||||||
&& this.frm.fields_dict.reconcile.$input.addClass("btn-primary");
|
|
||||||
this.frm.fields_dict.get_unreconciled_entries.$input
|
|
||||||
&& this.frm.fields_dict.get_unreconciled_entries.$input.removeClass("btn-primary");
|
|
||||||
} else {
|
|
||||||
this.frm.fields_dict.reconcile.$input
|
|
||||||
&& this.frm.fields_dict.reconcile.$input.removeClass("btn-primary");
|
|
||||||
this.frm.fields_dict.get_unreconciled_entries.$input
|
|
||||||
&& this.frm.fields_dict.get_unreconciled_entries.$input.addClass("btn-primary");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
|
$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
|
||||||
|
|||||||
@@ -1,622 +1,206 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 1,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
"allow_copy": 1,
|
||||||
"allow_guest_to_view": 0,
|
"creation": "2014-07-09 12:04:51.681583",
|
||||||
"allow_import": 0,
|
"doctype": "DocType",
|
||||||
"allow_rename": 0,
|
"engine": "InnoDB",
|
||||||
"beta": 0,
|
"field_order": [
|
||||||
"creation": "2014-07-09 12:04:51.681583",
|
"company",
|
||||||
"custom": 0,
|
"party_type",
|
||||||
"docstatus": 0,
|
"column_break_4",
|
||||||
"doctype": "DocType",
|
"party",
|
||||||
"document_type": "",
|
"receivable_payable_account",
|
||||||
"editable_grid": 0,
|
"col_break1",
|
||||||
"engine": "InnoDB",
|
"from_invoice_date",
|
||||||
|
"to_invoice_date",
|
||||||
|
"minimum_invoice_amount",
|
||||||
|
"maximum_invoice_amount",
|
||||||
|
"invoice_limit",
|
||||||
|
"column_break_13",
|
||||||
|
"from_payment_date",
|
||||||
|
"to_payment_date",
|
||||||
|
"minimum_payment_amount",
|
||||||
|
"maximum_payment_amount",
|
||||||
|
"payment_limit",
|
||||||
|
"bank_cash_account",
|
||||||
|
"sec_break1",
|
||||||
|
"invoices",
|
||||||
|
"column_break_15",
|
||||||
|
"payments",
|
||||||
|
"sec_break2",
|
||||||
|
"allocation"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "company",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"label": "Company",
|
||||||
"bold": 0,
|
"options": "Company",
|
||||||
"collapsible": 0,
|
"reqd": 1
|
||||||
"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,
|
|
||||||
"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,
|
"fieldname": "party_type",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"label": "Party Type",
|
||||||
"bold": 0,
|
"options": "DocType",
|
||||||
"collapsible": 0,
|
"reqd": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "party_type",
|
|
||||||
"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": "Party Type",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "DocType",
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"depends_on": "eval:doc.party_type",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "party",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Dynamic Link",
|
||||||
"bold": 0,
|
"label": "Party",
|
||||||
"collapsible": 0,
|
"options": "party_type",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"depends_on": "",
|
},
|
||||||
"fieldname": "party",
|
|
||||||
"fieldtype": "Dynamic 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": "Party",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "party_type",
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"depends_on": "eval:doc.company && doc.party",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "receivable_payable_account",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"label": "Receivable / Payable Account",
|
||||||
"collapsible": 0,
|
"options": "Account",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"fieldname": "receivable_payable_account",
|
},
|
||||||
"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": "Receivable / Payable Account",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Account",
|
|
||||||
"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,
|
"description": "This filter will be applied to Journal Entry.",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "bank_cash_account",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Bank / Cash Account",
|
||||||
"columns": 0,
|
"options": "Account"
|
||||||
"fieldname": "bank_cash_account",
|
},
|
||||||
"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": "Bank / Cash Account",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Account",
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"collapsible": 1,
|
||||||
"allow_in_quick_entry": 0,
|
"collapsible_depends_on": "eval: doc.invoices.length == 0",
|
||||||
"allow_on_submit": 0,
|
"depends_on": "eval:doc.receivable_payable_account",
|
||||||
"bold": 0,
|
"fieldname": "col_break1",
|
||||||
"collapsible": 0,
|
"fieldtype": "Section Break",
|
||||||
"columns": 0,
|
"label": "Filters"
|
||||||
"fieldname": "col_break1",
|
},
|
||||||
"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,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"depends_on": "eval:(doc.payments).length || (doc.invoices).length",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "sec_break1",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Section Break",
|
||||||
"bold": 0,
|
"label": "Unreconciled Entries"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "from_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": "From Invoice Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"fieldname": "payments",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Table",
|
||||||
"allow_on_submit": 0,
|
"label": "Payments",
|
||||||
"bold": 0,
|
"options": "Payment Reconciliation Payment"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "to_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": "To Invoice Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"depends_on": "allocation",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "sec_break2",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Section Break",
|
||||||
"bold": 0,
|
"label": "Allocated Entries"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "minimum_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": "Minimum Invoice Amount",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"fieldname": "invoices",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Table",
|
||||||
"allow_on_submit": 0,
|
"label": "Invoices",
|
||||||
"bold": 0,
|
"options": "Payment Reconciliation Invoice"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "maximum_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": "Maximum Invoice Amount",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"fieldname": "column_break_15",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"description": "System will fetch all the entries if limit value is zero.",
|
|
||||||
"fieldname": "limit",
|
|
||||||
"fieldtype": "Int",
|
|
||||||
"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": "Limit",
|
|
||||||
"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,
|
"fieldname": "allocation",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Table",
|
||||||
"allow_on_submit": 0,
|
"label": "Allocation",
|
||||||
"bold": 0,
|
"options": "Payment Reconciliation Allocation"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "get_unreconciled_entries",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"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": "Get Unreconciled Entries",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"fieldname": "column_break_4",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "sec_break1",
|
|
||||||
"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": "Unreconciled Payment Details",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"fieldname": "from_invoice_date",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Date",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "From Invoice Date"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "payments",
|
|
||||||
"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": "Payments",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Payment Reconciliation Payment",
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"fieldname": "to_invoice_date",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Date",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "To Invoice Date"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "reconcile",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"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": "Reconcile",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"fieldname": "minimum_invoice_amount",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Currency",
|
||||||
"allow_on_submit": 0,
|
"label": "Minimum Invoice Amount"
|
||||||
"bold": 0,
|
},
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "sec_break2",
|
|
||||||
"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": "Invoice/Journal Entry Details",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
"description": "System will fetch all the entries if limit value is zero.",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "invoice_limit",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Int",
|
||||||
"bold": 0,
|
"label": "Invoice Limit"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
{
|
||||||
"fieldname": "invoices",
|
"fieldname": "column_break_13",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
},
|
||||||
"ignore_user_permissions": 0,
|
{
|
||||||
"ignore_xss_filter": 0,
|
"fieldname": "from_payment_date",
|
||||||
"in_filter": 0,
|
"fieldtype": "Date",
|
||||||
"in_global_search": 0,
|
"label": "From Payment Date"
|
||||||
"in_list_view": 0,
|
},
|
||||||
"in_standard_filter": 0,
|
{
|
||||||
"label": "Invoices",
|
"fieldname": "to_payment_date",
|
||||||
"length": 0,
|
"fieldtype": "Date",
|
||||||
"no_copy": 0,
|
"label": "To Payment Date"
|
||||||
"options": "Payment Reconciliation Invoice",
|
},
|
||||||
"permlevel": 0,
|
{
|
||||||
"print_hide": 0,
|
"fieldname": "minimum_payment_amount",
|
||||||
"print_hide_if_no_value": 0,
|
"fieldtype": "Currency",
|
||||||
"read_only": 1,
|
"label": "Minimum Payment Amount"
|
||||||
"remember_last_selected_value": 0,
|
},
|
||||||
"report_hide": 0,
|
{
|
||||||
"reqd": 0,
|
"fieldname": "maximum_payment_amount",
|
||||||
"search_index": 0,
|
"fieldtype": "Currency",
|
||||||
"set_only_once": 0,
|
"label": "Maximum Payment Amount"
|
||||||
"translatable": 0,
|
},
|
||||||
"unique": 0
|
{
|
||||||
|
"fieldname": "payment_limit",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Payment Limit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "maximum_invoice_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Maximum Invoice Amount"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"hide_toolbar": 1,
|
||||||
"hide_heading": 0,
|
"icon": "icon-resize-horizontal",
|
||||||
"hide_toolbar": 1,
|
"issingle": 1,
|
||||||
"icon": "icon-resize-horizontal",
|
"links": [],
|
||||||
"idx": 0,
|
"modified": "2021-08-30 13:05:51.977861",
|
||||||
"image_view": 0,
|
"modified_by": "Administrator",
|
||||||
"in_create": 0,
|
"module": "Accounts",
|
||||||
"is_submittable": 0,
|
"name": "Payment Reconciliation",
|
||||||
"issingle": 1,
|
"owner": "Administrator",
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"menu_index": 0,
|
|
||||||
"modified": "2019-01-15 17:42:21.135214",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Payment Reconciliation",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"delete": 1,
|
||||||
"create": 1,
|
"read": 1,
|
||||||
"delete": 1,
|
"role": "Accounts Manager",
|
||||||
"email": 0,
|
"share": 1,
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"delete": 1,
|
||||||
"create": 1,
|
"read": 1,
|
||||||
"delete": 1,
|
"role": "Accounts User",
|
||||||
"email": 0,
|
"share": 1,
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "Accounts User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "DESC",
|
||||||
"read_only_onload": 0,
|
"track_changes": 1
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe.utils import flt, today
|
from frappe.utils import flt, today, getdate, nowdate
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.accounts.utils import (get_outstanding_invoices,
|
from erpnext.accounts.utils import (get_outstanding_invoices,
|
||||||
@@ -27,24 +27,32 @@ class PaymentReconciliation(Document):
|
|||||||
else:
|
else:
|
||||||
dr_or_cr_notes = []
|
dr_or_cr_notes = []
|
||||||
|
|
||||||
self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes)
|
non_reconciled_payments = payment_entries + journal_entries + dr_or_cr_notes
|
||||||
|
|
||||||
|
if self.payment_limit:
|
||||||
|
non_reconciled_payments = non_reconciled_payments[:self.payment_limit]
|
||||||
|
|
||||||
|
non_reconciled_payments = sorted(non_reconciled_payments, key=lambda k: k['posting_date'] or getdate(nowdate()))
|
||||||
|
|
||||||
|
self.add_payment_entries(non_reconciled_payments)
|
||||||
|
|
||||||
def get_payment_entries(self):
|
def get_payment_entries(self):
|
||||||
order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order"
|
order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order"
|
||||||
|
condition = self.get_conditions(get_payments=True)
|
||||||
payment_entries = get_advance_payment_entries(self.party_type, self.party,
|
payment_entries = get_advance_payment_entries(self.party_type, self.party,
|
||||||
self.receivable_payable_account, order_doctype, against_all_orders=True, limit=self.limit)
|
self.receivable_payable_account, order_doctype, against_all_orders=True, limit=self.payment_limit,
|
||||||
|
condition=condition)
|
||||||
|
|
||||||
return payment_entries
|
return payment_entries
|
||||||
|
|
||||||
def get_jv_entries(self):
|
def get_jv_entries(self):
|
||||||
|
condition = self.get_conditions()
|
||||||
dr_or_cr = ("credit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
|
dr_or_cr = ("credit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
|
||||||
else "debit_in_account_currency")
|
else "debit_in_account_currency")
|
||||||
|
|
||||||
bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
|
bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
|
||||||
if self.bank_cash_account else "1=1"
|
if self.bank_cash_account else "1=1"
|
||||||
|
|
||||||
limit_cond = "limit %s" % self.limit if self.limit else ""
|
|
||||||
|
|
||||||
journal_entries = frappe.db.sql("""
|
journal_entries = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
"Journal Entry" as reference_type, t1.name as reference_name,
|
"Journal Entry" as reference_type, t1.name as reference_name,
|
||||||
@@ -56,7 +64,7 @@ class PaymentReconciliation(Document):
|
|||||||
where
|
where
|
||||||
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
|
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
|
||||||
and t2.party_type = %(party_type)s and t2.party = %(party)s
|
and t2.party_type = %(party_type)s and t2.party = %(party)s
|
||||||
and t2.account = %(account)s and {dr_or_cr} > 0
|
and t2.account = %(account)s and {dr_or_cr} > 0 {condition}
|
||||||
and (t2.reference_type is null or t2.reference_type = '' or
|
and (t2.reference_type is null or t2.reference_type = '' or
|
||||||
(t2.reference_type in ('Sales Order', 'Purchase Order')
|
(t2.reference_type in ('Sales Order', 'Purchase Order')
|
||||||
and t2.reference_name is not null and t2.reference_name != ''))
|
and t2.reference_name is not null and t2.reference_name != ''))
|
||||||
@@ -65,11 +73,11 @@ class PaymentReconciliation(Document):
|
|||||||
THEN 1=1
|
THEN 1=1
|
||||||
ELSE {bank_account_condition}
|
ELSE {bank_account_condition}
|
||||||
END)
|
END)
|
||||||
order by t1.posting_date {limit_cond}
|
order by t1.posting_date
|
||||||
""".format(**{
|
""".format(**{
|
||||||
"dr_or_cr": dr_or_cr,
|
"dr_or_cr": dr_or_cr,
|
||||||
"bank_account_condition": bank_account_condition,
|
"bank_account_condition": bank_account_condition,
|
||||||
"limit_cond": limit_cond
|
"condition": condition
|
||||||
}), {
|
}), {
|
||||||
"party_type": self.party_type,
|
"party_type": self.party_type,
|
||||||
"party": self.party,
|
"party": self.party,
|
||||||
@@ -80,6 +88,7 @@ class PaymentReconciliation(Document):
|
|||||||
return list(journal_entries)
|
return list(journal_entries)
|
||||||
|
|
||||||
def get_dr_or_cr_notes(self):
|
def get_dr_or_cr_notes(self):
|
||||||
|
condition = self.get_conditions(get_return_invoices=True)
|
||||||
dr_or_cr = ("credit_in_account_currency"
|
dr_or_cr = ("credit_in_account_currency"
|
||||||
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
||||||
|
|
||||||
@@ -90,7 +99,7 @@ class PaymentReconciliation(Document):
|
|||||||
if self.party_type == 'Customer' else "Purchase Invoice")
|
if self.party_type == 'Customer' else "Purchase Invoice")
|
||||||
|
|
||||||
return frappe.db.sql(""" SELECT doc.name as reference_name, %(voucher_type)s as reference_type,
|
return frappe.db.sql(""" SELECT doc.name as reference_name, %(voucher_type)s as reference_type,
|
||||||
(sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount,
|
(sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount, doc.posting_date,
|
||||||
account_currency as currency
|
account_currency as currency
|
||||||
FROM `tab{doc}` doc, `tabGL Entry` gl
|
FROM `tab{doc}` doc, `tabGL Entry` gl
|
||||||
WHERE
|
WHERE
|
||||||
@@ -100,15 +109,17 @@ class PaymentReconciliation(Document):
|
|||||||
and gl.against_voucher_type = %(voucher_type)s
|
and gl.against_voucher_type = %(voucher_type)s
|
||||||
and doc.docstatus = 1 and gl.party = %(party)s
|
and doc.docstatus = 1 and gl.party = %(party)s
|
||||||
and gl.party_type = %(party_type)s and gl.account = %(account)s
|
and gl.party_type = %(party_type)s and gl.account = %(account)s
|
||||||
and gl.is_cancelled = 0
|
and gl.is_cancelled = 0 {condition}
|
||||||
GROUP BY doc.name
|
GROUP BY doc.name
|
||||||
Having
|
Having
|
||||||
amount > 0
|
amount > 0
|
||||||
|
ORDER BY doc.posting_date
|
||||||
""".format(
|
""".format(
|
||||||
doc=voucher_type,
|
doc=voucher_type,
|
||||||
dr_or_cr=dr_or_cr,
|
dr_or_cr=dr_or_cr,
|
||||||
reconciled_dr_or_cr=reconciled_dr_or_cr,
|
reconciled_dr_or_cr=reconciled_dr_or_cr,
|
||||||
party_type_field=frappe.scrub(self.party_type)),
|
party_type_field=frappe.scrub(self.party_type),
|
||||||
|
condition=condition or ""),
|
||||||
{
|
{
|
||||||
'party': self.party,
|
'party': self.party,
|
||||||
'party_type': self.party_type,
|
'party_type': self.party_type,
|
||||||
@@ -116,22 +127,23 @@ class PaymentReconciliation(Document):
|
|||||||
'account': self.receivable_payable_account
|
'account': self.receivable_payable_account
|
||||||
}, as_dict=1)
|
}, as_dict=1)
|
||||||
|
|
||||||
def add_payment_entries(self, entries):
|
def add_payment_entries(self, non_reconciled_payments):
|
||||||
self.set('payments', [])
|
self.set('payments', [])
|
||||||
for e in entries:
|
|
||||||
|
for payment in non_reconciled_payments:
|
||||||
row = self.append('payments', {})
|
row = self.append('payments', {})
|
||||||
row.update(e)
|
row.update(payment)
|
||||||
|
|
||||||
def get_invoice_entries(self):
|
def get_invoice_entries(self):
|
||||||
#Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
|
#Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
|
||||||
|
|
||||||
condition = self.check_condition()
|
condition = self.get_conditions(get_invoices=True)
|
||||||
|
|
||||||
non_reconciled_invoices = get_outstanding_invoices(self.party_type, self.party,
|
non_reconciled_invoices = get_outstanding_invoices(self.party_type, self.party,
|
||||||
self.receivable_payable_account, condition=condition)
|
self.receivable_payable_account, condition=condition)
|
||||||
|
|
||||||
if self.limit:
|
if self.invoice_limit:
|
||||||
non_reconciled_invoices = non_reconciled_invoices[:self.limit]
|
non_reconciled_invoices = non_reconciled_invoices[:self.invoice_limit]
|
||||||
|
|
||||||
self.add_invoice_entries(non_reconciled_invoices)
|
self.add_invoice_entries(non_reconciled_invoices)
|
||||||
|
|
||||||
@@ -139,41 +151,78 @@ class PaymentReconciliation(Document):
|
|||||||
#Populate 'invoices' with JVs and Invoices to reconcile against
|
#Populate 'invoices' with JVs and Invoices to reconcile against
|
||||||
self.set('invoices', [])
|
self.set('invoices', [])
|
||||||
|
|
||||||
for e in non_reconciled_invoices:
|
for entry in non_reconciled_invoices:
|
||||||
ent = self.append('invoices', {})
|
inv = self.append('invoices', {})
|
||||||
ent.invoice_type = e.get('voucher_type')
|
inv.invoice_type = entry.get('voucher_type')
|
||||||
ent.invoice_number = e.get('voucher_no')
|
inv.invoice_number = entry.get('voucher_no')
|
||||||
ent.invoice_date = e.get('posting_date')
|
inv.invoice_date = entry.get('posting_date')
|
||||||
ent.amount = flt(e.get('invoice_amount'))
|
inv.amount = flt(entry.get('invoice_amount'))
|
||||||
ent.currency = e.get('currency')
|
inv.currency = entry.get('currency')
|
||||||
ent.outstanding_amount = e.get('outstanding_amount')
|
inv.outstanding_amount = flt(entry.get('outstanding_amount'))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def reconcile(self, args):
|
def allocate_entries(self, args):
|
||||||
for e in self.get('payments'):
|
self.validate_entries()
|
||||||
e.invoice_type = None
|
entries = []
|
||||||
if e.invoice_number and " | " in e.invoice_number:
|
for pay in args.get('payments'):
|
||||||
e.invoice_type, e.invoice_number = e.invoice_number.split(" | ")
|
pay.update({'unreconciled_amount': pay.get('amount')})
|
||||||
|
for inv in args.get('invoices'):
|
||||||
|
if pay.get('amount') >= inv.get('outstanding_amount'):
|
||||||
|
res = self.get_allocated_entry(pay, inv, inv['outstanding_amount'])
|
||||||
|
pay['amount'] = flt(pay.get('amount')) - flt(inv.get('outstanding_amount'))
|
||||||
|
inv['outstanding_amount'] = 0
|
||||||
|
else:
|
||||||
|
res = self.get_allocated_entry(pay, inv, pay['amount'])
|
||||||
|
inv['outstanding_amount'] = flt(inv.get('outstanding_amount')) - flt(pay.get('amount'))
|
||||||
|
pay['amount'] = 0
|
||||||
|
if pay.get('amount') == 0:
|
||||||
|
entries.append(res)
|
||||||
|
break
|
||||||
|
elif inv.get('outstanding_amount') == 0:
|
||||||
|
entries.append(res)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
self.get_invoice_entries()
|
self.set('allocation', [])
|
||||||
self.validate_invoice()
|
for entry in entries:
|
||||||
|
if entry['allocated_amount'] != 0:
|
||||||
|
row = self.append('allocation', {})
|
||||||
|
row.update(entry)
|
||||||
|
|
||||||
|
def get_allocated_entry(self, pay, inv, allocated_amount):
|
||||||
|
return frappe._dict({
|
||||||
|
'reference_type': pay.get('reference_type'),
|
||||||
|
'reference_name': pay.get('reference_name'),
|
||||||
|
'reference_row': pay.get('reference_row'),
|
||||||
|
'invoice_type': inv.get('invoice_type'),
|
||||||
|
'invoice_number': inv.get('invoice_number'),
|
||||||
|
'unreconciled_amount': pay.get('unreconciled_amount'),
|
||||||
|
'amount': pay.get('amount'),
|
||||||
|
'allocated_amount': allocated_amount,
|
||||||
|
'difference_amount': pay.get('difference_amount')
|
||||||
|
})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def reconcile(self):
|
||||||
|
self.validate_allocation()
|
||||||
dr_or_cr = ("credit_in_account_currency"
|
dr_or_cr = ("credit_in_account_currency"
|
||||||
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
||||||
|
|
||||||
lst = []
|
entry_list = []
|
||||||
dr_or_cr_notes = []
|
dr_or_cr_notes = []
|
||||||
for e in self.get('payments'):
|
for row in self.get('allocation'):
|
||||||
reconciled_entry = []
|
reconciled_entry = []
|
||||||
if e.invoice_number and e.allocated_amount:
|
if row.invoice_number and row.allocated_amount:
|
||||||
if e.reference_type in ['Sales Invoice', 'Purchase Invoice']:
|
if row.reference_type in ['Sales Invoice', 'Purchase Invoice']:
|
||||||
reconciled_entry = dr_or_cr_notes
|
reconciled_entry = dr_or_cr_notes
|
||||||
else:
|
else:
|
||||||
reconciled_entry = lst
|
reconciled_entry = entry_list
|
||||||
|
|
||||||
reconciled_entry.append(self.get_payment_details(e, dr_or_cr))
|
reconciled_entry.append(self.get_payment_details(row, dr_or_cr))
|
||||||
|
|
||||||
if lst:
|
if entry_list:
|
||||||
reconcile_against_document(lst)
|
reconcile_against_document(entry_list)
|
||||||
|
|
||||||
if dr_or_cr_notes:
|
if dr_or_cr_notes:
|
||||||
reconcile_dr_cr_note(dr_or_cr_notes, self.company)
|
reconcile_dr_cr_note(dr_or_cr_notes, self.company)
|
||||||
@@ -183,98 +232,104 @@ class PaymentReconciliation(Document):
|
|||||||
|
|
||||||
def get_payment_details(self, row, dr_or_cr):
|
def get_payment_details(self, row, dr_or_cr):
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
'voucher_type': row.reference_type,
|
'voucher_type': row.get('reference_type'),
|
||||||
'voucher_no' : row.reference_name,
|
'voucher_no' : row.get('reference_name'),
|
||||||
'voucher_detail_no' : row.reference_row,
|
'voucher_detail_no' : row.get('reference_row'),
|
||||||
'against_voucher_type' : row.invoice_type,
|
'against_voucher_type' : row.get('invoice_type'),
|
||||||
'against_voucher' : row.invoice_number,
|
'against_voucher' : row.get('invoice_number'),
|
||||||
'account' : self.receivable_payable_account,
|
'account' : self.receivable_payable_account,
|
||||||
'party_type': self.party_type,
|
'party_type': self.party_type,
|
||||||
'party': self.party,
|
'party': self.party,
|
||||||
'is_advance' : row.is_advance,
|
'is_advance' : row.get('is_advance'),
|
||||||
'dr_or_cr' : dr_or_cr,
|
'dr_or_cr' : dr_or_cr,
|
||||||
'unadjusted_amount' : flt(row.amount),
|
'unreconciled_amount': flt(row.get('unreconciled_amount')),
|
||||||
'allocated_amount' : flt(row.allocated_amount),
|
'unadjusted_amount' : flt(row.get('amount')),
|
||||||
'difference_amount': row.difference_amount,
|
'allocated_amount' : flt(row.get('allocated_amount')),
|
||||||
'difference_account': row.difference_account
|
'difference_amount': flt(row.get('difference_amount')),
|
||||||
|
'difference_account': row.get('difference_account')
|
||||||
})
|
})
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_difference_amount(self, child_row):
|
|
||||||
if child_row.get("reference_type") != 'Payment Entry': return
|
|
||||||
|
|
||||||
child_row = frappe._dict(child_row)
|
|
||||||
|
|
||||||
if child_row.invoice_number and " | " in child_row.invoice_number:
|
|
||||||
child_row.invoice_type, child_row.invoice_number = child_row.invoice_number.split(" | ")
|
|
||||||
|
|
||||||
dr_or_cr = ("credit_in_account_currency"
|
|
||||||
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
|
||||||
|
|
||||||
row = self.get_payment_details(child_row, dr_or_cr)
|
|
||||||
|
|
||||||
doc = frappe.get_doc(row.voucher_type, row.voucher_no)
|
|
||||||
update_reference_in_payment_entry(row, doc, do_not_save=True)
|
|
||||||
|
|
||||||
return doc.difference_amount
|
|
||||||
|
|
||||||
def check_mandatory_to_fetch(self):
|
def check_mandatory_to_fetch(self):
|
||||||
for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
|
for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
|
||||||
if not self.get(fieldname):
|
if not self.get(fieldname):
|
||||||
frappe.throw(_("Please select {0} first").format(self.meta.get_label(fieldname)))
|
frappe.throw(_("Please select {0} first").format(self.meta.get_label(fieldname)))
|
||||||
|
|
||||||
def validate_invoice(self):
|
def validate_entries(self):
|
||||||
if not self.get("invoices"):
|
if not self.get("invoices"):
|
||||||
frappe.throw(_("No records found in the Invoice table"))
|
frappe.throw(_("No records found in the Invoices table"))
|
||||||
|
|
||||||
if not self.get("payments"):
|
if not self.get("payments"):
|
||||||
frappe.throw(_("No records found in the Payment table"))
|
frappe.throw(_("No records found in the Payments table"))
|
||||||
|
|
||||||
|
def validate_allocation(self):
|
||||||
unreconciled_invoices = frappe._dict()
|
unreconciled_invoices = frappe._dict()
|
||||||
for d in self.get("invoices"):
|
|
||||||
unreconciled_invoices.setdefault(d.invoice_type, {}).setdefault(d.invoice_number, d.outstanding_amount)
|
for inv in self.get("invoices"):
|
||||||
|
unreconciled_invoices.setdefault(inv.invoice_type, {}).setdefault(inv.invoice_number, inv.outstanding_amount)
|
||||||
|
|
||||||
invoices_to_reconcile = []
|
invoices_to_reconcile = []
|
||||||
for p in self.get("payments"):
|
for row in self.get("allocation"):
|
||||||
if p.invoice_type and p.invoice_number and p.allocated_amount:
|
if row.invoice_type and row.invoice_number and row.allocated_amount:
|
||||||
invoices_to_reconcile.append(p.invoice_number)
|
invoices_to_reconcile.append(row.invoice_number)
|
||||||
|
|
||||||
if p.invoice_number not in unreconciled_invoices.get(p.invoice_type, {}):
|
if flt(row.amount) - flt(row.allocated_amount) < 0:
|
||||||
frappe.throw(_("{0}: {1} not found in Invoice Details table")
|
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equal to remaining payment amount {2}")
|
||||||
.format(p.invoice_type, p.invoice_number))
|
.format(row.idx, row.allocated_amount, row.amount))
|
||||||
|
|
||||||
if flt(p.allocated_amount) > flt(p.amount):
|
invoice_outstanding = unreconciled_invoices.get(row.invoice_type, {}).get(row.invoice_number)
|
||||||
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2}")
|
if flt(row.allocated_amount) - invoice_outstanding > 0.009:
|
||||||
.format(p.idx, p.allocated_amount, p.amount))
|
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equal to invoice outstanding amount {2}")
|
||||||
|
.format(row.idx, row.allocated_amount, invoice_outstanding))
|
||||||
invoice_outstanding = unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number)
|
|
||||||
if flt(p.allocated_amount) - invoice_outstanding > 0.009:
|
|
||||||
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2}")
|
|
||||||
.format(p.idx, p.allocated_amount, invoice_outstanding))
|
|
||||||
|
|
||||||
if not invoices_to_reconcile:
|
if not invoices_to_reconcile:
|
||||||
frappe.throw(_("Please select Allocated Amount, Invoice Type and Invoice Number in atleast one row"))
|
frappe.throw(_("No records found in Allocation table"))
|
||||||
|
|
||||||
def check_condition(self):
|
def get_conditions(self, get_invoices=False, get_payments=False, get_return_invoices=False):
|
||||||
cond = " and posting_date >= {0}".format(frappe.db.escape(self.from_date)) if self.from_date else ""
|
condition = " and company = '{0}' ".format(self.company)
|
||||||
cond += " and posting_date <= {0}".format(frappe.db.escape(self.to_date)) if self.to_date else ""
|
|
||||||
dr_or_cr = ("debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
|
|
||||||
else "credit_in_account_currency")
|
|
||||||
|
|
||||||
if self.minimum_amount:
|
if get_invoices:
|
||||||
cond += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_amount))
|
condition += " and posting_date >= {0}".format(frappe.db.escape(self.from_invoice_date)) if self.from_invoice_date else ""
|
||||||
if self.maximum_amount:
|
condition += " and posting_date <= {0}".format(frappe.db.escape(self.to_invoice_date)) if self.to_invoice_date else ""
|
||||||
cond += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_amount))
|
dr_or_cr = ("debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
|
||||||
|
else "credit_in_account_currency")
|
||||||
|
|
||||||
return cond
|
if self.minimum_invoice_amount:
|
||||||
|
condition += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_invoice_amount))
|
||||||
|
if self.maximum_invoice_amount:
|
||||||
|
condition += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_invoice_amount))
|
||||||
|
|
||||||
|
elif get_return_invoices:
|
||||||
|
condition = " and doc.company = '{0}' ".format(self.company)
|
||||||
|
condition += " and doc.posting_date >= {0}".format(frappe.db.escape(self.from_payment_date)) if self.from_payment_date else ""
|
||||||
|
condition += " and doc.posting_date <= {0}".format(frappe.db.escape(self.to_payment_date)) if self.to_payment_date else ""
|
||||||
|
dr_or_cr = ("gl.debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
|
||||||
|
else "gl.credit_in_account_currency")
|
||||||
|
|
||||||
|
if self.minimum_invoice_amount:
|
||||||
|
condition += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_payment_amount))
|
||||||
|
if self.maximum_invoice_amount:
|
||||||
|
condition += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_payment_amount))
|
||||||
|
|
||||||
|
else:
|
||||||
|
condition += " and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date)) if self.from_payment_date else ""
|
||||||
|
condition += " and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date)) if self.to_payment_date else ""
|
||||||
|
|
||||||
|
if self.minimum_payment_amount:
|
||||||
|
condition += " and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount)) if get_payments \
|
||||||
|
else " and total_debit >= {0}".format(flt(self.minimum_payment_amount))
|
||||||
|
if self.maximum_payment_amount:
|
||||||
|
condition += " and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount)) if get_payments \
|
||||||
|
else " and total_debit <= {0}".format(flt(self.maximum_payment_amount))
|
||||||
|
|
||||||
|
return condition
|
||||||
|
|
||||||
def reconcile_dr_cr_note(dr_cr_notes, company):
|
def reconcile_dr_cr_note(dr_cr_notes, company):
|
||||||
for d in dr_cr_notes:
|
for inv in dr_cr_notes:
|
||||||
voucher_type = ('Credit Note'
|
voucher_type = ('Credit Note'
|
||||||
if d.voucher_type == 'Sales Invoice' else 'Debit Note')
|
if inv.voucher_type == 'Sales Invoice' else 'Debit Note')
|
||||||
|
|
||||||
reconcile_dr_or_cr = ('debit_in_account_currency'
|
reconcile_dr_or_cr = ('debit_in_account_currency'
|
||||||
if d.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency')
|
if inv.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency')
|
||||||
|
|
||||||
company_currency = erpnext.get_company_currency(company)
|
company_currency = erpnext.get_company_currency(company)
|
||||||
|
|
||||||
@@ -283,25 +338,25 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
|
|||||||
"voucher_type": voucher_type,
|
"voucher_type": voucher_type,
|
||||||
"posting_date": today(),
|
"posting_date": today(),
|
||||||
"company": company,
|
"company": company,
|
||||||
"multi_currency": 1 if d.currency != company_currency else 0,
|
"multi_currency": 1 if inv.currency != company_currency else 0,
|
||||||
"accounts": [
|
"accounts": [
|
||||||
{
|
{
|
||||||
'account': d.account,
|
'account': inv.account,
|
||||||
'party': d.party,
|
'party': inv.party,
|
||||||
'party_type': d.party_type,
|
'party_type': inv.party_type,
|
||||||
d.dr_or_cr: abs(d.allocated_amount),
|
inv.dr_or_cr: abs(inv.allocated_amount),
|
||||||
'reference_type': d.against_voucher_type,
|
'reference_type': inv.against_voucher_type,
|
||||||
'reference_name': d.against_voucher,
|
'reference_name': inv.against_voucher,
|
||||||
'cost_center': erpnext.get_default_cost_center(company)
|
'cost_center': erpnext.get_default_cost_center(company)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'account': d.account,
|
'account': inv.account,
|
||||||
'party': d.party,
|
'party': inv.party,
|
||||||
'party_type': d.party_type,
|
'party_type': inv.party_type,
|
||||||
reconcile_dr_or_cr: (abs(d.allocated_amount)
|
reconcile_dr_or_cr: (abs(inv.allocated_amount)
|
||||||
if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)),
|
if abs(inv.unadjusted_amount) > abs(inv.allocated_amount) else abs(inv.unadjusted_amount)),
|
||||||
'reference_type': d.voucher_type,
|
'reference_type': inv.voucher_type,
|
||||||
'reference_name': d.voucher_no,
|
'reference_name': inv.voucher_no,
|
||||||
'cost_center': erpnext.get_default_cost_center(company)
|
'cost_center': erpnext.get_default_cost_center(company)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestPaymentReconciliation(unittest.TestCase):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2021-08-16 17:04:40.185167",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"reference_type",
|
||||||
|
"reference_name",
|
||||||
|
"column_break_3",
|
||||||
|
"invoice_type",
|
||||||
|
"invoice_number",
|
||||||
|
"section_break_6",
|
||||||
|
"allocated_amount",
|
||||||
|
"unreconciled_amount",
|
||||||
|
"amount",
|
||||||
|
"column_break_8",
|
||||||
|
"is_advance",
|
||||||
|
"section_break_5",
|
||||||
|
"difference_amount",
|
||||||
|
"column_break_7",
|
||||||
|
"difference_account"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "invoice_number",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Invoice Number",
|
||||||
|
"options": "invoice_type",
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "allocated_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Allocated Amount",
|
||||||
|
"options": "Currency",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_5",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "difference_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Difference Account",
|
||||||
|
"options": "Account",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_7",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "difference_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Difference Amount",
|
||||||
|
"options": "Currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "reference_name",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Reference Name",
|
||||||
|
"options": "reference_type",
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "is_advance",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Is Advance",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "reference_type",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Reference Type",
|
||||||
|
"options": "DocType",
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "invoice_type",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Invoice Type",
|
||||||
|
"options": "DocType",
|
||||||
|
"read_only": 1,
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_6",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_8",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "unreconciled_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Unreconciled Amount",
|
||||||
|
"options": "Currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Amount",
|
||||||
|
"options": "Currency",
|
||||||
|
"read_only": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-08-30 10:58:42.665107",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Payment Reconciliation Allocation",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class PaymentReconciliationAllocation(Document):
|
||||||
|
pass
|
||||||
@@ -44,7 +44,6 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "amount",
|
"fieldname": "amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
@@ -67,7 +66,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-19 18:12:27.964073",
|
"modified": "2021-08-24 22:42:40.923179",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation Invoice",
|
"name": "Payment Reconciliation Invoice",
|
||||||
|
|||||||
@@ -11,11 +11,7 @@
|
|||||||
"is_advance",
|
"is_advance",
|
||||||
"reference_row",
|
"reference_row",
|
||||||
"col_break1",
|
"col_break1",
|
||||||
"invoice_number",
|
|
||||||
"amount",
|
"amount",
|
||||||
"allocated_amount",
|
|
||||||
"section_break_10",
|
|
||||||
"difference_account",
|
|
||||||
"difference_amount",
|
"difference_amount",
|
||||||
"sec_break1",
|
"sec_break1",
|
||||||
"remark",
|
"remark",
|
||||||
@@ -41,6 +37,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "posting_date",
|
"fieldname": "posting_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Posting Date",
|
"label": "Posting Date",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@@ -62,14 +59,6 @@
|
|||||||
"fieldname": "col_break1",
|
"fieldname": "col_break1",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "invoice_number",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Invoice Number",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fieldname": "amount",
|
"fieldname": "amount",
|
||||||
@@ -79,15 +68,6 @@
|
|||||||
"options": "currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "allocated_amount",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Allocated amount",
|
|
||||||
"options": "currency",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "sec_break1",
|
"fieldname": "sec_break1",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
@@ -95,41 +75,27 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "remark",
|
"fieldname": "remark",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Remark",
|
"label": "Remark",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "difference_account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Difference Account",
|
|
||||||
"options": "Account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "difference_amount",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"label": "Difference Amount",
|
|
||||||
"options": "currency",
|
|
||||||
"print_hide": 1,
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "section_break_10",
|
|
||||||
"fieldtype": "Section Break"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "currency",
|
"fieldname": "currency",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Currency",
|
"label": "Currency",
|
||||||
"options": "Currency"
|
"options": "Currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "difference_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Difference Amount",
|
||||||
|
"options": "currency",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-19 18:12:41.682347",
|
"modified": "2021-08-30 10:51:48.140062",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Reconciliation Payment",
|
"name": "Payment Reconciliation Payment",
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
if gl_entries:
|
if gl_entries:
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
make_gl_entries(gl_entries)
|
make_gl_entries(gl_entries)
|
||||||
|
|
||||||
def get_gl_entries(self):
|
def get_gl_entries(self):
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
pl_accounts = self.get_pl_balances()
|
pl_accounts = self.get_pl_balances()
|
||||||
@@ -77,7 +77,7 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
gl_entries += gle_for_net_pl_bal
|
gl_entries += gle_for_net_pl_bal
|
||||||
|
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
def get_pnl_gl_entry(self, pl_accounts):
|
def get_pnl_gl_entry(self, pl_accounts):
|
||||||
company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
|||||||
@@ -203,13 +203,13 @@ def apply_pricing_rule(args, doc=None):
|
|||||||
serialized_items = dict()
|
serialized_items = dict()
|
||||||
for item_code, val in query_items:
|
for item_code, val in query_items:
|
||||||
serialized_items.setdefault(item_code, val)
|
serialized_items.setdefault(item_code, val)
|
||||||
|
|
||||||
for item in item_list:
|
for item in item_list:
|
||||||
args_copy = copy.deepcopy(args)
|
args_copy = copy.deepcopy(args)
|
||||||
args_copy.update(item)
|
args_copy.update(item)
|
||||||
data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate'), doc=doc)
|
data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate'), doc=doc)
|
||||||
out.append(data)
|
out.append(data)
|
||||||
|
|
||||||
if serialized_items.get(item.get('item_code')) and not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'):
|
if serialized_items.get(item.get('item_code')) and not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'):
|
||||||
out[0].update(get_serial_no_for_item(args_copy))
|
out[0].update(get_serial_no_for_item(args_copy))
|
||||||
|
|
||||||
@@ -315,9 +315,8 @@ def update_args_for_pricing_rule(args):
|
|||||||
if not (args.item_group and args.brand):
|
if not (args.item_group and args.brand):
|
||||||
try:
|
try:
|
||||||
args.item_group, args.brand = frappe.get_cached_value("Item", args.item_code, ["item_group", "brand"])
|
args.item_group, args.brand = frappe.get_cached_value("Item", args.item_code, ["item_group", "brand"])
|
||||||
except TypeError:
|
except frappe.DoesNotExistError:
|
||||||
# invalid item_code
|
return
|
||||||
return item_details
|
|
||||||
if not args.item_group:
|
if not args.item_group:
|
||||||
frappe.throw(_("Item Group not mentioned in item master for item {0}").format(args.item_code))
|
frappe.throw(_("Item Group not mentioned in item master for item {0}").format(args.item_code))
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ def filter_pricing_rule_based_on_condition(pricing_rules, doc=None):
|
|||||||
try:
|
try:
|
||||||
if frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()):
|
if frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()):
|
||||||
filtered_pricing_rules.append(pricing_rule)
|
filtered_pricing_rules.append(pricing_rule)
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
filtered_pricing_rules.append(pricing_rule)
|
filtered_pricing_rules.append(pricing_rule)
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ def get_recipients_and_cc(customer, doc):
|
|||||||
if doc.cc_to != '':
|
if doc.cc_to != '':
|
||||||
try:
|
try:
|
||||||
cc=[frappe.get_value('User', doc.cc_to, 'email')]
|
cc=[frappe.get_value('User', doc.cc_to, 'email')]
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return recipients, cc
|
return recipients, cc
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
|||||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||||
from frappe.model.utils import get_fetch_values
|
from frappe.model.utils import get_fetch_values
|
||||||
from frappe.contacts.doctype.address.address import get_address_display
|
from frappe.contacts.doctype.address.address import get_address_display
|
||||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
|
||||||
|
|
||||||
from erpnext.healthcare.utils import manage_invoice_submit_cancel
|
from erpnext.healthcare.utils import manage_invoice_submit_cancel
|
||||||
|
|
||||||
@@ -933,7 +932,7 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
if asset.calculate_depreciation:
|
if asset.calculate_depreciation:
|
||||||
self.reset_depreciation_schedule(asset)
|
self.reset_depreciation_schedule(asset)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
|
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
|
||||||
item.base_net_amount, item.finance_book)
|
item.base_net_amount, item.finance_book)
|
||||||
@@ -947,7 +946,7 @@ class SalesInvoice(SellingController):
|
|||||||
gl_entries.append(self.get_gl_dict(gle, item=item))
|
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||||
|
|
||||||
self.set_asset_status(asset)
|
self.set_asset_status(asset)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Do not book income for transfer within same company
|
# Do not book income for transfer within same company
|
||||||
if not self.is_internal_transfer():
|
if not self.is_internal_transfer():
|
||||||
@@ -980,7 +979,7 @@ class SalesInvoice(SellingController):
|
|||||||
asset = frappe.get_doc("Asset", item.asset)
|
asset = frappe.get_doc("Asset", item.asset)
|
||||||
else:
|
else:
|
||||||
frappe.throw(_(
|
frappe.throw(_(
|
||||||
"Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
|
"Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
|
||||||
title=_("Missing Asset")
|
title=_("Missing Asset")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -997,7 +996,7 @@ class SalesInvoice(SellingController):
|
|||||||
asset.flags.ignore_validate_update_after_submit = True
|
asset.flags.ignore_validate_update_after_submit = True
|
||||||
asset.prepare_depreciation_data(self.posting_date)
|
asset.prepare_depreciation_data(self.posting_date)
|
||||||
asset.save()
|
asset.save()
|
||||||
|
|
||||||
post_depreciation_entries(self.posting_date)
|
post_depreciation_entries(self.posting_date)
|
||||||
|
|
||||||
def reset_depreciation_schedule(self, asset):
|
def reset_depreciation_schedule(self, asset):
|
||||||
@@ -1037,7 +1036,7 @@ class SalesInvoice(SellingController):
|
|||||||
finance_book = schedule.finance_book
|
finance_book = schedule.finance_book
|
||||||
else:
|
else:
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
if schedule.schedule_date == posting_date_of_original_invoice:
|
if schedule.schedule_date == posting_date_of_original_invoice:
|
||||||
if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice):
|
if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice):
|
||||||
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
|
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
|
||||||
@@ -1047,13 +1046,13 @@ class SalesInvoice(SellingController):
|
|||||||
def get_posting_date_of_sales_invoice(self):
|
def get_posting_date_of_sales_invoice(self):
|
||||||
return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
|
return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
|
||||||
|
|
||||||
# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
|
# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
|
||||||
def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice):
|
def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice):
|
||||||
for finance_book in asset.get('finance_books'):
|
for finance_book in asset.get('finance_books'):
|
||||||
if schedule.finance_book == finance_book.finance_book:
|
if schedule.finance_book == finance_book.finance_book:
|
||||||
orginal_schedule_date = add_months(finance_book.depreciation_start_date,
|
orginal_schedule_date = add_months(finance_book.depreciation_start_date,
|
||||||
row * cint(finance_book.frequency_of_depreciation))
|
row * cint(finance_book.frequency_of_depreciation))
|
||||||
|
|
||||||
if orginal_schedule_date == posting_date_of_original_invoice:
|
if orginal_schedule_date == posting_date_of_original_invoice:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -1118,9 +1118,9 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
'qty': 1
|
'qty': 1
|
||||||
})
|
})
|
||||||
pi.set_missing_values()
|
pi.set_missing_values()
|
||||||
|
|
||||||
asset = create_asset(item_code="Macbook Pro")
|
asset = create_asset(item_code="Macbook Pro")
|
||||||
|
|
||||||
si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000)
|
si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000)
|
||||||
return_si = create_sales_invoice(is_return=1, return_against=si.name, item_code="Macbook Pro", asset=asset.name, qty=-1, rate=90000)
|
return_si = create_sales_invoice(is_return=1, return_against=si.name, item_code="Macbook Pro", asset=asset.name, qty=-1, rate=90000)
|
||||||
|
|
||||||
@@ -1128,7 +1128,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
# Asset value is 100,000 but it was sold for 90,000, so there should be a loss of 10,000
|
# Asset value is 100,000 but it was sold for 90,000, so there should be a loss of 10,000
|
||||||
loss_for_si = frappe.get_all(
|
loss_for_si = frappe.get_all(
|
||||||
"GL Entry",
|
"GL Entry",
|
||||||
filters = {
|
filters = {
|
||||||
"voucher_no": si.name,
|
"voucher_no": si.name,
|
||||||
"account": disposal_account
|
"account": disposal_account
|
||||||
@@ -1137,7 +1137,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
loss_for_return_si = frappe.get_all(
|
loss_for_return_si = frappe.get_all(
|
||||||
"GL Entry",
|
"GL Entry",
|
||||||
filters = {
|
filters = {
|
||||||
"voucher_no": return_si.name,
|
"voucher_no": return_si.name,
|
||||||
"account": disposal_account
|
"account": disposal_account
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details):
|
|||||||
def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
|
def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
|
||||||
tds_amount = 0
|
tds_amount = 0
|
||||||
invoice_filters = {
|
invoice_filters = {
|
||||||
'name': ('in', vouchers),
|
'name': ('in', vouchers),
|
||||||
'docstatus': 1,
|
'docstatus': 1,
|
||||||
'apply_tds': 1
|
'apply_tds': 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -648,7 +648,7 @@ def get_default_contact(doctype, name):
|
|||||||
if out:
|
if out:
|
||||||
try:
|
try:
|
||||||
return out[0][0]
|
return out[0][0]
|
||||||
except:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ def sort_accounts(accounts, is_root=False, key="name"):
|
|||||||
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
|
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
|
||||||
|
|
||||||
def compare_accounts(a, b):
|
def compare_accounts(a, b):
|
||||||
if re.split('\W+', a[key])[0].isdigit():
|
if re.split(r'\W+', a[key])[0].isdigit():
|
||||||
# if chart of accounts is numbered, then sort by number
|
# if chart of accounts is numbered, then sort by number
|
||||||
return cmp(a[key], b[key])
|
return cmp(a[key], b[key])
|
||||||
elif is_root:
|
elif is_root:
|
||||||
|
|||||||
@@ -341,31 +341,42 @@ def add_cc(args=None):
|
|||||||
|
|
||||||
def reconcile_against_document(args):
|
def reconcile_against_document(args):
|
||||||
"""
|
"""
|
||||||
Cancel JV, Update aginst document, split if required and resubmit jv
|
Cancel PE or JV, Update against document, split if required and resubmit
|
||||||
"""
|
"""
|
||||||
for d in args:
|
# To optimize making GL Entry for PE or JV with multiple references
|
||||||
|
reconciled_entries = {}
|
||||||
|
for row in args:
|
||||||
|
if not reconciled_entries.get((row.voucher_type, row.voucher_no)):
|
||||||
|
reconciled_entries[(row.voucher_type, row.voucher_no)] = []
|
||||||
|
|
||||||
check_if_advance_entry_modified(d)
|
reconciled_entries[(row.voucher_type, row.voucher_no)].append(row)
|
||||||
validate_allocated_amount(d)
|
|
||||||
|
for key, entries in reconciled_entries.items():
|
||||||
|
voucher_type = key[0]
|
||||||
|
voucher_no = key[1]
|
||||||
|
|
||||||
# cancel advance entry
|
# cancel advance entry
|
||||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
doc = frappe.get_doc(voucher_type, voucher_no)
|
||||||
|
|
||||||
frappe.flags.ignore_party_validation = True
|
frappe.flags.ignore_party_validation = True
|
||||||
doc.make_gl_entries(cancel=1, adv_adj=1)
|
doc.make_gl_entries(cancel=1, adv_adj=1)
|
||||||
|
|
||||||
# update ref in advance entry
|
for entry in entries:
|
||||||
if d.voucher_type == "Journal Entry":
|
check_if_advance_entry_modified(entry)
|
||||||
update_reference_in_journal_entry(d, doc)
|
validate_allocated_amount(entry)
|
||||||
else:
|
|
||||||
update_reference_in_payment_entry(d, doc)
|
|
||||||
|
|
||||||
|
# update ref in advance entry
|
||||||
|
if voucher_type == "Journal Entry":
|
||||||
|
update_reference_in_journal_entry(entry, doc, do_not_save=True)
|
||||||
|
else:
|
||||||
|
update_reference_in_payment_entry(entry, doc, do_not_save=True)
|
||||||
|
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
# re-submit advance entry
|
# re-submit advance entry
|
||||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
|
||||||
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
||||||
frappe.flags.ignore_party_validation = False
|
frappe.flags.ignore_party_validation = False
|
||||||
|
|
||||||
if d.voucher_type in ('Payment Entry', 'Journal Entry'):
|
if entry.voucher_type in ('Payment Entry', 'Journal Entry'):
|
||||||
doc.update_expense_claim()
|
doc.update_expense_claim()
|
||||||
|
|
||||||
def check_if_advance_entry_modified(args):
|
def check_if_advance_entry_modified(args):
|
||||||
@@ -374,6 +385,9 @@ def check_if_advance_entry_modified(args):
|
|||||||
check if amount is same
|
check if amount is same
|
||||||
check if jv is submitted
|
check if jv is submitted
|
||||||
"""
|
"""
|
||||||
|
if not args.get('unreconciled_amount'):
|
||||||
|
args.update({'unreconciled_amount': args.get('unadjusted_amount')})
|
||||||
|
|
||||||
ret = None
|
ret = None
|
||||||
if args.voucher_type == "Journal Entry":
|
if args.voucher_type == "Journal Entry":
|
||||||
ret = frappe.db.sql("""
|
ret = frappe.db.sql("""
|
||||||
@@ -395,14 +409,14 @@ def check_if_advance_entry_modified(args):
|
|||||||
and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
|
and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
|
||||||
and t1.party_type = %(party_type)s and t1.party = %(party)s and t1.{0} = %(account)s
|
and t1.party_type = %(party_type)s and t1.party = %(party)s and t1.{0} = %(account)s
|
||||||
and t2.reference_doctype in ("", "Sales Order", "Purchase Order")
|
and t2.reference_doctype in ("", "Sales Order", "Purchase Order")
|
||||||
and t2.allocated_amount = %(unadjusted_amount)s
|
and t2.allocated_amount = %(unreconciled_amount)s
|
||||||
""".format(party_account_field), args)
|
""".format(party_account_field), args)
|
||||||
else:
|
else:
|
||||||
ret = frappe.db.sql("""select name from `tabPayment Entry`
|
ret = frappe.db.sql("""select name from `tabPayment Entry`
|
||||||
where
|
where
|
||||||
name = %(voucher_no)s and docstatus = 1
|
name = %(voucher_no)s and docstatus = 1
|
||||||
and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s
|
and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s
|
||||||
and unallocated_amount = %(unadjusted_amount)s
|
and unallocated_amount = %(unreconciled_amount)s
|
||||||
""".format(party_account_field), args)
|
""".format(party_account_field), args)
|
||||||
|
|
||||||
if not ret:
|
if not ret:
|
||||||
@@ -415,58 +429,44 @@ def validate_allocated_amount(args):
|
|||||||
elif flt(args.get("allocated_amount"), precision) > flt(args.get("unadjusted_amount"), precision):
|
elif flt(args.get("allocated_amount"), precision) > flt(args.get("unadjusted_amount"), precision):
|
||||||
throw(_("Allocated amount cannot be greater than unadjusted amount"))
|
throw(_("Allocated amount cannot be greater than unadjusted amount"))
|
||||||
|
|
||||||
def update_reference_in_journal_entry(d, jv_obj):
|
def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
||||||
"""
|
"""
|
||||||
Updates against document, if partial amount splits into rows
|
Updates against document, if partial amount splits into rows
|
||||||
"""
|
"""
|
||||||
jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
||||||
jv_detail.set(d["dr_or_cr"], d["allocated_amount"])
|
|
||||||
jv_detail.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit',
|
|
||||||
d["allocated_amount"]*flt(jv_detail.exchange_rate))
|
|
||||||
|
|
||||||
original_reference_type = jv_detail.reference_type
|
|
||||||
original_reference_name = jv_detail.reference_name
|
|
||||||
|
|
||||||
jv_detail.set("reference_type", d["against_voucher_type"])
|
|
||||||
jv_detail.set("reference_name", d["against_voucher"])
|
|
||||||
|
|
||||||
if d['allocated_amount'] < d['unadjusted_amount']:
|
|
||||||
jvd = frappe.db.sql("""
|
|
||||||
select cost_center, balance, against_account, is_advance,
|
|
||||||
account_type, exchange_rate, account_currency
|
|
||||||
from `tabJournal Entry Account` where name = %s
|
|
||||||
""", d['voucher_detail_no'], as_dict=True)
|
|
||||||
|
|
||||||
|
if flt(d['unadjusted_amount']) - flt(d['allocated_amount']) != 0:
|
||||||
|
# adjust the unreconciled balance
|
||||||
amount_in_account_currency = flt(d['unadjusted_amount']) - flt(d['allocated_amount'])
|
amount_in_account_currency = flt(d['unadjusted_amount']) - flt(d['allocated_amount'])
|
||||||
amount_in_company_currency = amount_in_account_currency * flt(jvd[0]['exchange_rate'])
|
amount_in_company_currency = amount_in_account_currency * flt(jv_detail.exchange_rate)
|
||||||
|
jv_detail.set(d['dr_or_cr'], amount_in_account_currency)
|
||||||
|
jv_detail.set('debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit', amount_in_company_currency)
|
||||||
|
else:
|
||||||
|
journal_entry.remove(jv_detail)
|
||||||
|
|
||||||
# new entry with balance amount
|
# new row with references
|
||||||
ch = jv_obj.append("accounts")
|
new_row = journal_entry.append("accounts")
|
||||||
ch.account = d['account']
|
new_row.update(jv_detail.as_dict().copy())
|
||||||
ch.account_type = jvd[0]['account_type']
|
|
||||||
ch.account_currency = jvd[0]['account_currency']
|
|
||||||
ch.exchange_rate = jvd[0]['exchange_rate']
|
|
||||||
ch.party_type = d["party_type"]
|
|
||||||
ch.party = d["party"]
|
|
||||||
ch.cost_center = cstr(jvd[0]["cost_center"])
|
|
||||||
ch.balance = flt(jvd[0]["balance"])
|
|
||||||
|
|
||||||
ch.set(d['dr_or_cr'], amount_in_account_currency)
|
new_row.set(d["dr_or_cr"], d["allocated_amount"])
|
||||||
ch.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit', amount_in_company_currency)
|
new_row.set('debit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'credit',
|
||||||
|
d["allocated_amount"] * flt(jv_detail.exchange_rate))
|
||||||
|
|
||||||
ch.set('credit_in_account_currency' if d['dr_or_cr']== 'debit_in_account_currency'
|
new_row.set('credit_in_account_currency' if d['dr_or_cr'] == 'debit_in_account_currency'
|
||||||
else 'debit_in_account_currency', 0)
|
else 'debit_in_account_currency', 0)
|
||||||
ch.set('credit' if d['dr_or_cr']== 'debit_in_account_currency' else 'debit', 0)
|
new_row.set('credit' if d['dr_or_cr'] == 'debit_in_account_currency' else 'debit', 0)
|
||||||
|
|
||||||
ch.against_account = cstr(jvd[0]["against_account"])
|
new_row.set("reference_type", d["against_voucher_type"])
|
||||||
ch.reference_type = original_reference_type
|
new_row.set("reference_name", d["against_voucher"])
|
||||||
ch.reference_name = original_reference_name
|
|
||||||
ch.is_advance = cstr(jvd[0]["is_advance"])
|
new_row.against_account = cstr(jv_detail.against_account)
|
||||||
ch.docstatus = 1
|
new_row.is_advance = cstr(jv_detail.is_advance)
|
||||||
|
new_row.docstatus = 1
|
||||||
|
|
||||||
# will work as update after submit
|
# will work as update after submit
|
||||||
jv_obj.flags.ignore_validate_update_after_submit = True
|
journal_entry.flags.ignore_validate_update_after_submit = True
|
||||||
jv_obj.save(ignore_permissions=True)
|
if not do_not_save:
|
||||||
|
journal_entry.save(ignore_permissions=True)
|
||||||
|
|
||||||
def update_reference_in_payment_entry(d, payment_entry, do_not_save=False):
|
def update_reference_in_payment_entry(d, payment_entry, do_not_save=False):
|
||||||
reference_details = {
|
reference_details = {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe import _
|
|
||||||
|
|
||||||
class Disease(Document):
|
class Disease(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import flt, cint
|
from frappe.utils import flt, cint
|
||||||
from frappe import _
|
|
||||||
|
|
||||||
class SoilTexture(Document):
|
class SoilTexture(Document):
|
||||||
soil_edit_order = [2, 1, 0]
|
soil_edit_order = [2, 1, 0]
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe import _
|
|
||||||
|
|
||||||
class WaterAnalysis(Document):
|
class WaterAnalysis(Document):
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from frappe.model.document import Document
|
|||||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
||||||
from erpnext.assets.doctype.asset.depreciation \
|
from erpnext.assets.doctype.asset.depreciation \
|
||||||
import get_disposal_account_and_cost_center, get_depreciation_accounts
|
import get_disposal_account_and_cost_center, get_depreciation_accounts
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
|
from erpnext.accounts.general_ledger import make_reverse_gl_entries
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
|
|
||||||
@@ -321,10 +321,10 @@ class Asset(AccountsController):
|
|||||||
def get_from_date(self, finance_book):
|
def get_from_date(self, finance_book):
|
||||||
if not self.get('schedules'):
|
if not self.get('schedules'):
|
||||||
return self.available_for_use_date
|
return self.available_for_use_date
|
||||||
|
|
||||||
if len(self.finance_books) == 1:
|
if len(self.finance_books) == 1:
|
||||||
return self.schedules[-1].schedule_date
|
return self.schedules[-1].schedule_date
|
||||||
|
|
||||||
from_date = ""
|
from_date = ""
|
||||||
for schedule in self.get('schedules'):
|
for schedule in self.get('schedules'):
|
||||||
if schedule.finance_book == finance_book:
|
if schedule.finance_book == finance_book:
|
||||||
@@ -546,7 +546,7 @@ class Asset(AccountsController):
|
|||||||
cwip_account = None
|
cwip_account = None
|
||||||
try:
|
try:
|
||||||
cwip_account = get_asset_account("capital_work_in_progress_account", self.name, self.asset_category, self.company)
|
cwip_account = get_asset_account("capital_work_in_progress_account", self.name, self.asset_category, self.company)
|
||||||
except:
|
except Exception:
|
||||||
# if no cwip account found in category or company and "cwip is enabled" then raise else silently pass
|
# if no cwip account found in category or company and "cwip is enabled" then raise else silently pass
|
||||||
if cwip_enabled:
|
if cwip_enabled:
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -1211,7 +1211,7 @@ class AccountsController(TransactionBase):
|
|||||||
d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount'))
|
d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount'))
|
||||||
d.outstanding = d.payment_amount
|
d.outstanding = d.payment_amount
|
||||||
elif not d.invoice_portion:
|
elif not d.invoice_portion:
|
||||||
d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount'))
|
d.base_payment_amount = flt(d.payment_amount * self.get("conversion_rate"), d.precision('base_payment_amount'))
|
||||||
|
|
||||||
|
|
||||||
def get_order_details(self):
|
def get_order_details(self):
|
||||||
@@ -1592,7 +1592,7 @@ def get_advance_journal_entries(party_type, party, party_account, amount_field,
|
|||||||
|
|
||||||
|
|
||||||
def get_advance_payment_entries(party_type, party, party_account, order_doctype,
|
def get_advance_payment_entries(party_type, party, party_account, order_doctype,
|
||||||
order_list=None, include_unallocated=True, against_all_orders=False, limit=None):
|
order_list=None, include_unallocated=True, against_all_orders=False, limit=None, condition=None):
|
||||||
party_account_field = "paid_from" if party_type == "Customer" else "paid_to"
|
party_account_field = "paid_from" if party_type == "Customer" else "paid_to"
|
||||||
currency_field = "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency"
|
currency_field = "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency"
|
||||||
payment_type = "Receive" if party_type == "Customer" else "Pay"
|
payment_type = "Receive" if party_type == "Customer" else "Pay"
|
||||||
@@ -1627,14 +1627,14 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype,
|
|||||||
|
|
||||||
if include_unallocated:
|
if include_unallocated:
|
||||||
unallocated_payment_entries = frappe.db.sql("""
|
unallocated_payment_entries = frappe.db.sql("""
|
||||||
select "Payment Entry" as reference_type, name as reference_name,
|
select "Payment Entry" as reference_type, name as reference_name, posting_date,
|
||||||
remarks, unallocated_amount as amount, {2} as exchange_rate
|
remarks, unallocated_amount as amount, {2} as exchange_rate, {3} as currency
|
||||||
from `tabPayment Entry`
|
from `tabPayment Entry`
|
||||||
where
|
where
|
||||||
{0} = %s and party_type = %s and party = %s and payment_type = %s
|
{0} = %s and party_type = %s and party = %s and payment_type = %s
|
||||||
and docstatus = 1 and unallocated_amount > 0
|
and docstatus = 1 and unallocated_amount > 0 {condition}
|
||||||
order by posting_date {1}
|
order by posting_date {1}
|
||||||
""".format(party_account_field, limit_cond, exchange_rate_field),
|
""".format(party_account_field, limit_cond, exchange_rate_field, currency_field, condition=condition or ""),
|
||||||
(party_account, party_type, party, payment_type), as_dict=1)
|
(party_account, party_type, party, payment_type), as_dict=1)
|
||||||
|
|
||||||
return list(payment_entries_against_order) + list(unallocated_payment_entries)
|
return list(payment_entries_against_order) + list(unallocated_payment_entries)
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ def validate_returned_items(doc):
|
|||||||
|
|
||||||
if doc.doctype in ("Delivery Note", "Sales Invoice"):
|
if doc.doctype in ("Delivery Note", "Sales Invoice"):
|
||||||
for d in frappe.db.sql("""select item_code, qty, serial_no, batch_no from `tabPacked Item`
|
for d in frappe.db.sql("""select item_code, qty, serial_no, batch_no from `tabPacked Item`
|
||||||
where parent = %s""".format(doc.doctype), doc.return_against, as_dict=1):
|
where parent = %s""", doc.return_against, as_dict=1):
|
||||||
valid_items = get_ref_item_dict(valid_items, d)
|
valid_items = get_ref_item_dict(valid_items, d)
|
||||||
|
|
||||||
already_returned_items = get_already_returned_items(doc)
|
already_returned_items = get_already_returned_items(doc)
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ class AppointmentBookingSettings(Document):
|
|||||||
to_time = datetime.datetime.strptime(
|
to_time = datetime.datetime.strptime(
|
||||||
self.min_date+record.to_time, self.format_string)
|
self.min_date+record.to_time, self.format_string)
|
||||||
timedelta = to_time-from_time
|
timedelta = to_time-from_time
|
||||||
self.validate_from_and_to_time(from_time, to_time)
|
self.validate_from_and_to_time(from_time, to_time, record)
|
||||||
self.duration_is_divisible(from_time, to_time)
|
self.duration_is_divisible(from_time, to_time)
|
||||||
|
|
||||||
def validate_from_and_to_time(self, from_time, to_time):
|
def validate_from_and_to_time(self, from_time, to_time, record):
|
||||||
if from_time > to_time:
|
if from_time > to_time:
|
||||||
err_msg = _('<b>From Time</b> cannot be later than <b>To Time</b> for {0}').format(record.day_of_week)
|
err_msg = _('<b>From Time</b> cannot be later than <b>To Time</b> for {0}').format(record.day_of_week)
|
||||||
frappe.throw(_(err_msg))
|
frappe.throw(_(err_msg))
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ class Lead(SellingController):
|
|||||||
"salutation": self.salutation,
|
"salutation": self.salutation,
|
||||||
"gender": self.gender,
|
"gender": self.gender,
|
||||||
"designation": self.designation,
|
"designation": self.designation,
|
||||||
|
"company_name": self.company_name,
|
||||||
})
|
})
|
||||||
|
|
||||||
if self.email_id:
|
if self.email_id:
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ class LinkedInSettings(Document):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.api_error(response)
|
self.api_error(response)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def get_headers(self):
|
def get_headers(self):
|
||||||
@@ -168,7 +168,7 @@ class LinkedInSettings(Document):
|
|||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
self.api_error(response)
|
self.api_error(response)
|
||||||
|
|
||||||
def get_post(self, post_id):
|
def get_post(self, post_id):
|
||||||
url = "https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:{0}&shares[0]=urn:li:share:{1}".format(self.company_id, post_id)
|
url = "https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:{0}&shares[0]=urn:li:share:{1}".format(self.company_id, post_id)
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ class LinkedInSettings(Document):
|
|||||||
response = requests.get(url=url, headers=self.get_headers())
|
response = requests.get(url=url, headers=self.get_headers())
|
||||||
if response.status_code !=200:
|
if response.status_code !=200:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
self.api_error(response)
|
self.api_error(response)
|
||||||
|
|
||||||
|
|||||||
@@ -80,10 +80,10 @@ frappe.ui.form.on('Social Media Post', {
|
|||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.trigger('text');
|
frm.trigger('text');
|
||||||
|
|
||||||
if (frm.doc.docstatus === 1) {
|
if (frm.doc.docstatus === 1) {
|
||||||
if (!['Posted', 'Deleted'].includes(frm.doc.post_status)) {
|
if (!['Posted', 'Deleted'].includes(frm.doc.post_status)) {
|
||||||
frm.trigger('add_post_btn');
|
frm.trigger('add_post_btn');
|
||||||
}
|
}
|
||||||
if (frm.doc.post_status !='Deleted') {
|
if (frm.doc.post_status !='Deleted') {
|
||||||
frm.add_custom_button(('Delete Post'), function() {
|
frm.add_custom_button(('Delete Post'), function() {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class SocialMediaPost(Document):
|
|||||||
if self.scheduled_time:
|
if self.scheduled_time:
|
||||||
self.post_status = "Scheduled"
|
self.post_status = "Scheduled"
|
||||||
super(SocialMediaPost, self).submit()
|
super(SocialMediaPost, self).submit()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.db_set('post_status', 'Cancelled')
|
self.db_set('post_status', 'Cancelled')
|
||||||
|
|
||||||
@@ -35,11 +35,11 @@ class SocialMediaPost(Document):
|
|||||||
if self.twitter and self.twitter_post_id:
|
if self.twitter and self.twitter_post_id:
|
||||||
twitter = frappe.get_doc("Twitter Settings")
|
twitter = frappe.get_doc("Twitter Settings")
|
||||||
twitter.delete_tweet(self.twitter_post_id)
|
twitter.delete_tweet(self.twitter_post_id)
|
||||||
|
|
||||||
if self.linkedin and self.linkedin_post_id:
|
if self.linkedin and self.linkedin_post_id:
|
||||||
linkedin = frappe.get_doc("LinkedIn Settings")
|
linkedin = frappe.get_doc("LinkedIn Settings")
|
||||||
linkedin.delete_post(self.linkedin_post_id)
|
linkedin.delete_post(self.linkedin_post_id)
|
||||||
|
|
||||||
self.db_set('post_status', 'Deleted')
|
self.db_set('post_status', 'Deleted')
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -51,7 +51,7 @@ class SocialMediaPost(Document):
|
|||||||
if self.twitter and self.twitter_post_id:
|
if self.twitter and self.twitter_post_id:
|
||||||
twitter = frappe.get_doc("Twitter Settings")
|
twitter = frappe.get_doc("Twitter Settings")
|
||||||
response['twitter'] = twitter.get_tweet(self.twitter_post_id)
|
response['twitter'] = twitter.get_tweet(self.twitter_post_id)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -67,7 +67,7 @@ class SocialMediaPost(Document):
|
|||||||
self.db_set("linkedin_post_id", linkedin_post.headers['X-RestLi-Id'])
|
self.db_set("linkedin_post_id", linkedin_post.headers['X-RestLi-Id'])
|
||||||
self.db_set("post_status", "Posted")
|
self.db_set("post_status", "Posted")
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
self.db_set("post_status", "Error")
|
self.db_set("post_status", "Error")
|
||||||
title = _("Error while POSTING {0}").format(self.name)
|
title = _("Error while POSTING {0}").format(self.name)
|
||||||
frappe.log_error(message=frappe.get_traceback(), title=title)
|
frappe.log_error(message=frappe.get_traceback(), title=title)
|
||||||
|
|||||||
@@ -53,10 +53,10 @@ class TwitterSettings(Document):
|
|||||||
frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
|
frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
|
||||||
|
|
||||||
def get_api(self):
|
def get_api(self):
|
||||||
# authentication of consumer key and secret
|
# authentication of consumer key and secret
|
||||||
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"))
|
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"))
|
||||||
# authentication of access token and secret
|
# authentication of access token and secret
|
||||||
auth.set_access_token(self.access_token, self.access_token_secret)
|
auth.set_access_token(self.access_token, self.access_token_secret)
|
||||||
|
|
||||||
return tweepy.API(auth)
|
return tweepy.API(auth)
|
||||||
|
|
||||||
@@ -90,20 +90,20 @@ class TwitterSettings(Document):
|
|||||||
|
|
||||||
def delete_tweet(self, tweet_id):
|
def delete_tweet(self, tweet_id):
|
||||||
api = self.get_api()
|
api = self.get_api()
|
||||||
try:
|
try:
|
||||||
api.destroy_status(tweet_id)
|
api.destroy_status(tweet_id)
|
||||||
except TweepError as e:
|
except TweepError as e:
|
||||||
self.api_error(e)
|
self.api_error(e)
|
||||||
|
|
||||||
def get_tweet(self, tweet_id):
|
def get_tweet(self, tweet_id):
|
||||||
api = self.get_api()
|
api = self.get_api()
|
||||||
try:
|
try:
|
||||||
response = api.get_status(tweet_id, trim_user=True, include_entities=True)
|
response = api.get_status(tweet_id, trim_user=True, include_entities=True)
|
||||||
except TweepError as e:
|
except TweepError as e:
|
||||||
self.api_error(e)
|
self.api_error(e)
|
||||||
|
|
||||||
return response._json
|
return response._json
|
||||||
|
|
||||||
def api_error(self, e):
|
def api_error(self, e):
|
||||||
content = json.loads(e.response.content)
|
content = json.loads(e.response.content)
|
||||||
content = content["errors"][0]
|
content = content["errors"][0]
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ def simulate(domain='Manufacturing', days=100):
|
|||||||
elif domain=='Education':
|
elif domain=='Education':
|
||||||
edu.work()
|
edu.work()
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
frappe.db.set_global('demo_last_date', current_date)
|
frappe.db.set_global('demo_last_date', current_date)
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class CourseSchedulingTool(Document):
|
|||||||
if self.day == calendar.day_name[getdate(d.schedule_date).weekday()]:
|
if self.day == calendar.day_name[getdate(d.schedule_date).weekday()]:
|
||||||
frappe.delete_doc("Course Schedule", d.name)
|
frappe.delete_doc("Course Schedule", d.name)
|
||||||
rescheduled.append(d.name)
|
rescheduled.append(d.name)
|
||||||
except:
|
except Exception:
|
||||||
reschedule_errors.append(d.name)
|
reschedule_errors.append(d.name)
|
||||||
return rescheduled, reschedule_errors
|
return rescheduled, reschedule_errors
|
||||||
|
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ def get_quiz(quiz_name, course):
|
|||||||
try:
|
try:
|
||||||
quiz = frappe.get_doc("Quiz", quiz_name)
|
quiz = frappe.get_doc("Quiz", quiz_name)
|
||||||
questions = quiz.get_questions()
|
questions = quiz.get_questions()
|
||||||
except:
|
except Exception:
|
||||||
frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError)
|
frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class xml2dict(object):
|
|||||||
ns = http://cs.sfsu.edu/csc867/myscheduler
|
ns = http://cs.sfsu.edu/csc867/myscheduler
|
||||||
name = patients
|
name = patients
|
||||||
"""
|
"""
|
||||||
result = re.compile("\{(.*)\}(.*)").search(tag)
|
result = re.compile(r"\{(.*)\}(.*)").search(tag)
|
||||||
if result:
|
if result:
|
||||||
value.namespace, tag = result.groups()
|
value.namespace, tag = result.groups()
|
||||||
|
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ class TallyMigration(Document):
|
|||||||
|
|
||||||
self.is_master_data_processed = 1
|
self.is_master_data_processed = 1
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
self.publish("Process Master Data", _("Process Failed"), -1, 5)
|
self.publish("Process Master Data", _("Process Failed"), -1, 5)
|
||||||
self.log()
|
self.log()
|
||||||
|
|
||||||
@@ -302,14 +302,14 @@ class TallyMigration(Document):
|
|||||||
try:
|
try:
|
||||||
party_doc = frappe.get_doc(party)
|
party_doc = frappe.get_doc(party)
|
||||||
party_doc.insert()
|
party_doc.insert()
|
||||||
except:
|
except Exception:
|
||||||
self.log(party_doc)
|
self.log(party_doc)
|
||||||
addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url})
|
addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url})
|
||||||
for address in json.loads(addresses_file.get_content()):
|
for address in json.loads(addresses_file.get_content()):
|
||||||
try:
|
try:
|
||||||
address_doc = frappe.get_doc(address)
|
address_doc = frappe.get_doc(address)
|
||||||
address_doc.insert(ignore_mandatory=True)
|
address_doc.insert(ignore_mandatory=True)
|
||||||
except:
|
except Exception:
|
||||||
self.log(address_doc)
|
self.log(address_doc)
|
||||||
|
|
||||||
def create_items_uoms(items_file_url, uoms_file_url):
|
def create_items_uoms(items_file_url, uoms_file_url):
|
||||||
@@ -319,7 +319,7 @@ class TallyMigration(Document):
|
|||||||
try:
|
try:
|
||||||
uom_doc = frappe.get_doc(uom)
|
uom_doc = frappe.get_doc(uom)
|
||||||
uom_doc.insert()
|
uom_doc.insert()
|
||||||
except:
|
except Exception:
|
||||||
self.log(uom_doc)
|
self.log(uom_doc)
|
||||||
|
|
||||||
items_file = frappe.get_doc("File", {"file_url": items_file_url})
|
items_file = frappe.get_doc("File", {"file_url": items_file_url})
|
||||||
@@ -327,7 +327,7 @@ class TallyMigration(Document):
|
|||||||
try:
|
try:
|
||||||
item_doc = frappe.get_doc(item)
|
item_doc = frappe.get_doc(item)
|
||||||
item_doc.insert()
|
item_doc.insert()
|
||||||
except:
|
except Exception:
|
||||||
self.log(item_doc)
|
self.log(item_doc)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -346,7 +346,7 @@ class TallyMigration(Document):
|
|||||||
self.is_master_data_imported = 1
|
self.is_master_data_imported = 1
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
self.publish("Import Master Data", _("Process Failed"), -1, 5)
|
self.publish("Import Master Data", _("Process Failed"), -1, 5)
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
self.log()
|
self.log()
|
||||||
@@ -370,7 +370,7 @@ class TallyMigration(Document):
|
|||||||
if processed_voucher:
|
if processed_voucher:
|
||||||
vouchers.append(processed_voucher)
|
vouchers.append(processed_voucher)
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except:
|
except Exception:
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
self.log(voucher)
|
self.log(voucher)
|
||||||
return vouchers
|
return vouchers
|
||||||
@@ -494,7 +494,7 @@ class TallyMigration(Document):
|
|||||||
|
|
||||||
self.is_day_book_data_processed = 1
|
self.is_day_book_data_processed = 1
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
self.publish("Process Day Book Data", _("Process Failed"), -1, 5)
|
self.publish("Process Day Book Data", _("Process Failed"), -1, 5)
|
||||||
self.log()
|
self.log()
|
||||||
|
|
||||||
@@ -564,7 +564,7 @@ class TallyMigration(Document):
|
|||||||
is_last = True
|
is_last = True
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last)
|
frappe.enqueue_doc(self.doctype, self.name, "_import_vouchers", queue="long", timeout=3600, start=index+1, total=total, is_last=is_last)
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
self.log()
|
self.log()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
@@ -583,7 +583,7 @@ class TallyMigration(Document):
|
|||||||
voucher_doc.submit()
|
voucher_doc.submit()
|
||||||
self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total)
|
self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total)
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except:
|
except Exception:
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
self.log(voucher_doc)
|
self.log(voucher_doc)
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class HealthcareServiceUnit(NestedSet):
|
|||||||
self.validate_one_root()
|
self.validate_one_root()
|
||||||
|
|
||||||
def set_service_unit_properties(self):
|
def set_service_unit_properties(self):
|
||||||
if self.is_group:
|
if cint(self.is_group):
|
||||||
self.allow_appointments = False
|
self.allow_appointments = False
|
||||||
self.overlap_appointments = False
|
self.overlap_appointments = False
|
||||||
self.inpatient_occupancy = False
|
self.inpatient_occupancy = False
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class LabTest(Document):
|
|||||||
if item.result_value and item.secondary_uom and item.conversion_factor:
|
if item.result_value and item.secondary_uom and item.conversion_factor:
|
||||||
try:
|
try:
|
||||||
item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor)
|
item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor)
|
||||||
except:
|
except Exception:
|
||||||
item.secondary_uom_result = ''
|
item.secondary_uom_result = ''
|
||||||
frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated').format(item.idx), title = _('Warning'))
|
frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated').format(item.idx), title = _('Warning'))
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
import json
|
import json
|
||||||
from frappe.utils import getdate, get_time, flt
|
from frappe.utils import getdate, get_time, flt, get_link_to_form
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe import _
|
from frappe import _
|
||||||
import datetime
|
import datetime
|
||||||
@@ -333,17 +333,13 @@ def check_employee_wise_availability(date, practitioner_doc):
|
|||||||
|
|
||||||
|
|
||||||
def get_available_slots(practitioner_doc, date):
|
def get_available_slots(practitioner_doc, date):
|
||||||
available_slots = []
|
available_slots = slot_details = []
|
||||||
slot_details = []
|
|
||||||
weekday = date.strftime('%A')
|
weekday = date.strftime('%A')
|
||||||
practitioner = practitioner_doc.name
|
practitioner = practitioner_doc.name
|
||||||
|
|
||||||
for schedule_entry in practitioner_doc.practitioner_schedules:
|
for schedule_entry in practitioner_doc.practitioner_schedules:
|
||||||
if schedule_entry.schedule:
|
validate_practitioner_schedules(schedule_entry, practitioner)
|
||||||
practitioner_schedule = frappe.get_doc('Practitioner Schedule', schedule_entry.schedule)
|
practitioner_schedule = frappe.get_doc('Practitioner Schedule', schedule_entry.schedule)
|
||||||
else:
|
|
||||||
frappe.throw(_('{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner').format(
|
|
||||||
frappe.bold(practitioner)), title=_('Practitioner Schedule Not Found'))
|
|
||||||
|
|
||||||
if practitioner_schedule:
|
if practitioner_schedule:
|
||||||
available_slots = []
|
available_slots = []
|
||||||
@@ -386,6 +382,19 @@ def get_available_slots(practitioner_doc, date):
|
|||||||
return slot_details
|
return slot_details
|
||||||
|
|
||||||
|
|
||||||
|
def validate_practitioner_schedules(schedule_entry, practitioner):
|
||||||
|
if schedule_entry.schedule:
|
||||||
|
if not schedule_entry.service_unit:
|
||||||
|
frappe.throw(_('Practitioner {0} does not have a Service Unit set against the Practitioner Schedule {1}.').format(
|
||||||
|
get_link_to_form('Healthcare Practitioner', practitioner), frappe.bold(schedule_entry.schedule)),
|
||||||
|
title=_('Service Unit Not Found'))
|
||||||
|
|
||||||
|
else:
|
||||||
|
frappe.throw(_('Practitioner {0} does not have a Practitioner Schedule assigned.').format(
|
||||||
|
get_link_to_form('Healthcare Practitioner', practitioner)),
|
||||||
|
title=_('Practitioner Schedule Not Found'))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_status(appointment_id, status):
|
def update_status(appointment_id, status):
|
||||||
frappe.db.set_value('Patient Appointment', appointment_id, 'status', status)
|
frappe.db.set_value('Patient Appointment', appointment_id, 'status', status)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cstr, getdate, add_days
|
from frappe.utils import cstr, getdate, add_days
|
||||||
from frappe import _
|
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class TestTherapyPlan(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
|
self.assertEqual(frappe.db.get_value('Therapy Plan', plan.name, 'status'), 'Completed')
|
||||||
|
|
||||||
patient, practitioner = create_healthcare_docs()
|
patient, practitioner = create_healthcare_docs()
|
||||||
appointment = create_appointment(patient, practitioner, nowdate())
|
appointment = create_appointment(patient, practitioner, nowdate())
|
||||||
|
|
||||||
session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
|
session = make_therapy_session(plan.name, plan.patient, 'Basic Rehab', '_Test Company', appointment.name)
|
||||||
session = frappe.get_doc(session)
|
session = frappe.get_doc(session)
|
||||||
|
|||||||
@@ -243,6 +243,11 @@ doc_events = {
|
|||||||
"on_update": ["erpnext.hr.doctype.employee.employee.update_user_permissions",
|
"on_update": ["erpnext.hr.doctype.employee.employee.update_user_permissions",
|
||||||
"erpnext.portal.utils.set_default_role"]
|
"erpnext.portal.utils.set_default_role"]
|
||||||
},
|
},
|
||||||
|
"Communication": {
|
||||||
|
"on_update": [
|
||||||
|
"erpnext.support.doctype.issue.issue.set_first_response_time"
|
||||||
|
]
|
||||||
|
},
|
||||||
"Sales Taxes and Charges Template": {
|
"Sales Taxes and Charges Template": {
|
||||||
"on_update": "erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings.validate_cart_settings"
|
"on_update": "erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings.validate_cart_settings"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class DailyWorkSummary(Document):
|
|||||||
crop=True
|
crop=True
|
||||||
)
|
)
|
||||||
d.image = thumbnail_image
|
d.image = thumbnail_image
|
||||||
except:
|
except Exception:
|
||||||
d.image = original_image
|
d.image = original_image
|
||||||
|
|
||||||
if d.sender in did_not_reply:
|
if d.sender in did_not_reply:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class TestEmployeeReminders(unittest.TestCase):
|
|||||||
# Create a test holiday list
|
# Create a test holiday list
|
||||||
test_holiday_dates = cls.get_test_holiday_dates()
|
test_holiday_dates = cls.get_test_holiday_dates()
|
||||||
test_holiday_list = make_holiday_list(
|
test_holiday_list = make_holiday_list(
|
||||||
'TestHolidayRemindersList',
|
'TestHolidayRemindersList',
|
||||||
holiday_dates=[
|
holiday_dates=[
|
||||||
{'holiday_date': test_holiday_dates[0], 'description': 'test holiday1'},
|
{'holiday_date': test_holiday_dates[0], 'description': 'test holiday1'},
|
||||||
{'holiday_date': test_holiday_dates[1], 'description': 'test holiday2'},
|
{'holiday_date': test_holiday_dates[1], 'description': 'test holiday2'},
|
||||||
@@ -49,8 +49,8 @@ class TestEmployeeReminders(unittest.TestCase):
|
|||||||
def get_test_holiday_dates(cls):
|
def get_test_holiday_dates(cls):
|
||||||
today_date = getdate()
|
today_date = getdate()
|
||||||
return [
|
return [
|
||||||
today_date,
|
today_date,
|
||||||
today_date-timedelta(days=4),
|
today_date-timedelta(days=4),
|
||||||
today_date-timedelta(days=3),
|
today_date-timedelta(days=3),
|
||||||
today_date+timedelta(days=1),
|
today_date+timedelta(days=1),
|
||||||
today_date+timedelta(days=3),
|
today_date+timedelta(days=3),
|
||||||
@@ -63,7 +63,7 @@ class TestEmployeeReminders(unittest.TestCase):
|
|||||||
|
|
||||||
def test_is_holiday(self):
|
def test_is_holiday(self):
|
||||||
from erpnext.hr.doctype.employee.employee import is_holiday
|
from erpnext.hr.doctype.employee.employee import is_holiday
|
||||||
|
|
||||||
self.assertTrue(is_holiday(self.test_employee.name))
|
self.assertTrue(is_holiday(self.test_employee.name))
|
||||||
self.assertTrue(is_holiday(self.test_employee.name, date=self.test_holiday_dates[1]))
|
self.assertTrue(is_holiday(self.test_employee.name, date=self.test_holiday_dates[1]))
|
||||||
self.assertFalse(is_holiday(self.test_employee.name, date=getdate()-timedelta(days=1)))
|
self.assertFalse(is_holiday(self.test_employee.name, date=getdate()-timedelta(days=1)))
|
||||||
@@ -118,7 +118,7 @@ class TestEmployeeReminders(unittest.TestCase):
|
|||||||
|
|
||||||
email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
|
email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
|
||||||
self.assertTrue("Subject: Work Anniversary Reminder" in email_queue[0].message)
|
self.assertTrue("Subject: Work Anniversary Reminder" in email_queue[0].message)
|
||||||
|
|
||||||
def test_send_holidays_reminder_in_advance(self):
|
def test_send_holidays_reminder_in_advance(self):
|
||||||
from erpnext.hr.utils import get_holidays_for_employee
|
from erpnext.hr.utils import get_holidays_for_employee
|
||||||
from erpnext.hr.doctype.employee.employee_reminders import send_holidays_reminder_in_advance
|
from erpnext.hr.doctype.employee.employee_reminders import send_holidays_reminder_in_advance
|
||||||
@@ -133,10 +133,10 @@ class TestEmployeeReminders(unittest.TestCase):
|
|||||||
holidays = get_holidays_for_employee(
|
holidays = get_holidays_for_employee(
|
||||||
self.test_employee.get('name'),
|
self.test_employee.get('name'),
|
||||||
getdate(), getdate() + timedelta(days=3),
|
getdate(), getdate() + timedelta(days=3),
|
||||||
only_non_weekly=True,
|
only_non_weekly=True,
|
||||||
raise_exception=False
|
raise_exception=False
|
||||||
)
|
)
|
||||||
|
|
||||||
send_holidays_reminder_in_advance(
|
send_holidays_reminder_in_advance(
|
||||||
self.test_employee.get('name'),
|
self.test_employee.get('name'),
|
||||||
holidays
|
holidays
|
||||||
@@ -158,7 +158,7 @@ class TestEmployeeReminders(unittest.TestCase):
|
|||||||
|
|
||||||
email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
|
email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True)
|
||||||
self.assertTrue(len(email_queue) > 0)
|
self.assertTrue(len(email_queue) > 0)
|
||||||
|
|
||||||
def test_advance_holiday_reminders_weekly(self):
|
def test_advance_holiday_reminders_weekly(self):
|
||||||
from erpnext.hr.doctype.employee.employee_reminders import send_reminders_in_advance_weekly
|
from erpnext.hr.doctype.employee.employee_reminders import send_reminders_in_advance_weekly
|
||||||
# Get HR settings and enable advance holiday reminders
|
# Get HR settings and enable advance holiday reminders
|
||||||
|
|||||||
@@ -144,20 +144,20 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
expense_claim = make_expense_claim(payable_account, 5500, 5500, "_Test Company", "Travel Expenses - _TC")
|
expense_claim = make_expense_claim(payable_account, 5500, 5500, "_Test Company", "Travel Expenses - _TC")
|
||||||
expense_claim.save()
|
expense_claim.save()
|
||||||
expense_claim.submit()
|
expense_claim.submit()
|
||||||
|
|
||||||
# Payment entry 1: paying 500
|
# Payment entry 1: paying 500
|
||||||
make_payment_entry(expense_claim, payable_account,500)
|
make_payment_entry(expense_claim, payable_account,500)
|
||||||
outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(expense_claim)
|
outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(expense_claim)
|
||||||
self.assertEqual(outstanding_amount, 5000)
|
self.assertEqual(outstanding_amount, 5000)
|
||||||
self.assertEqual(total_amount_reimbursed, 500)
|
self.assertEqual(total_amount_reimbursed, 500)
|
||||||
|
|
||||||
# Payment entry 1: paying 2000
|
# Payment entry 1: paying 2000
|
||||||
make_payment_entry(expense_claim, payable_account,2000)
|
make_payment_entry(expense_claim, payable_account,2000)
|
||||||
outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(expense_claim)
|
outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(expense_claim)
|
||||||
self.assertEqual(outstanding_amount, 3000)
|
self.assertEqual(outstanding_amount, 3000)
|
||||||
self.assertEqual(total_amount_reimbursed, 2500)
|
self.assertEqual(total_amount_reimbursed, 2500)
|
||||||
|
|
||||||
# Payment entry 1: paying 3000
|
# Payment entry 1: paying 3000
|
||||||
make_payment_entry(expense_claim, payable_account,3000)
|
make_payment_entry(expense_claim, payable_account,3000)
|
||||||
outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(expense_claim)
|
outstanding_amount, total_amount_reimbursed = get_outstanding_and_total_reimbursed_amounts(expense_claim)
|
||||||
self.assertEqual(outstanding_amount, 0)
|
self.assertEqual(outstanding_amount, 0)
|
||||||
@@ -221,7 +221,7 @@ def get_outstanding_and_total_reimbursed_amounts(expense_claim):
|
|||||||
outstanding_amount = flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_sanctioned_amount")) - \
|
outstanding_amount = flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_sanctioned_amount")) - \
|
||||||
flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed"))
|
flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed"))
|
||||||
total_amount_reimbursed = flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed"))
|
total_amount_reimbursed = flt(frappe.db.get_value("Expense Claim", expense_claim.name, "total_amount_reimbursed"))
|
||||||
|
|
||||||
return outstanding_amount,total_amount_reimbursed
|
return outstanding_amount,total_amount_reimbursed
|
||||||
|
|
||||||
def make_payment_entry(expense_claim, payable_account, amt):
|
def make_payment_entry(expense_claim, payable_account, amt):
|
||||||
@@ -234,5 +234,5 @@ def make_payment_entry(expense_claim, payable_account, amt):
|
|||||||
pe.paid_to = payable_account
|
pe.paid_to = payable_account
|
||||||
pe.references[0].allocated_amount = amt
|
pe.references[0].allocated_amount = amt
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
|
|||||||
@@ -662,26 +662,30 @@ def is_lwp(leave_type):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_events(start, end, filters=None):
|
def get_events(start, end, filters=None):
|
||||||
|
from frappe.desk.reportview import get_filters_cond
|
||||||
events = []
|
events = []
|
||||||
|
|
||||||
employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}, ["name", "company"],
|
employee = frappe.db.get_value("Employee",
|
||||||
as_dict=True)
|
filters={"user_id": frappe.session.user},
|
||||||
|
fieldname=["name", "company"],
|
||||||
|
as_dict=True
|
||||||
|
)
|
||||||
|
|
||||||
if employee:
|
if employee:
|
||||||
employee, company = employee.name, employee.company
|
employee, company = employee.name, employee.company
|
||||||
else:
|
else:
|
||||||
employee=''
|
employee = ''
|
||||||
company=frappe.db.get_value("Global Defaults", None, "default_company")
|
company = frappe.db.get_value("Global Defaults", None, "default_company")
|
||||||
|
|
||||||
from frappe.desk.reportview import get_filters_cond
|
|
||||||
conditions = get_filters_cond("Leave Application", filters, [])
|
conditions = get_filters_cond("Leave Application", filters, [])
|
||||||
# show department leaves for employee
|
# show department leaves for employee
|
||||||
if "Employee" in frappe.get_roles():
|
if "Employee" in frappe.get_roles():
|
||||||
add_department_leaves(events, start, end, employee, company)
|
add_department_leaves(events, start, end, employee, company)
|
||||||
|
|
||||||
add_leaves(events, start, end, conditions)
|
add_leaves(events, start, end, conditions)
|
||||||
|
|
||||||
add_block_dates(events, start, end, employee, company)
|
add_block_dates(events, start, end, employee, company)
|
||||||
add_holidays(events, start, end, employee, company)
|
add_holidays(events, start, end, employee, company)
|
||||||
|
|
||||||
return events
|
return events
|
||||||
|
|
||||||
def add_department_leaves(events, start, end, employee, company):
|
def add_department_leaves(events, start, end, employee, company):
|
||||||
@@ -697,26 +701,37 @@ def add_department_leaves(events, start, end, employee, company):
|
|||||||
filter_conditions = " and employee in (\"%s\")" % '", "'.join(department_employees)
|
filter_conditions = " and employee in (\"%s\")" % '", "'.join(department_employees)
|
||||||
add_leaves(events, start, end, filter_conditions=filter_conditions)
|
add_leaves(events, start, end, filter_conditions=filter_conditions)
|
||||||
|
|
||||||
|
|
||||||
def add_leaves(events, start, end, filter_conditions=None):
|
def add_leaves(events, start, end, filter_conditions=None):
|
||||||
|
from frappe.desk.reportview import build_match_conditions
|
||||||
conditions = []
|
conditions = []
|
||||||
|
|
||||||
|
|
||||||
if not cint(frappe.db.get_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar")):
|
if not cint(frappe.db.get_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar")):
|
||||||
from frappe.desk.reportview import build_match_conditions
|
|
||||||
match_conditions = build_match_conditions("Leave Application")
|
match_conditions = build_match_conditions("Leave Application")
|
||||||
|
|
||||||
if match_conditions:
|
if match_conditions:
|
||||||
conditions.append(match_conditions)
|
conditions.append(match_conditions)
|
||||||
|
|
||||||
query = """select name, from_date, to_date, employee_name, half_day,
|
query = """SELECT
|
||||||
status, employee, docstatus
|
docstatus,
|
||||||
from `tabLeave Application` where
|
name,
|
||||||
from_date <= %(end)s and to_date >= %(start)s <= to_date
|
employee,
|
||||||
and docstatus < 2
|
employee_name,
|
||||||
and status!='Rejected' """
|
leave_type,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
half_day,
|
||||||
|
status,
|
||||||
|
color
|
||||||
|
FROM `tabLeave Application`
|
||||||
|
WHERE
|
||||||
|
from_date <= %(end)s AND to_date >= %(start)s <= to_date
|
||||||
|
AND docstatus < 2
|
||||||
|
AND status != 'Rejected'
|
||||||
|
"""
|
||||||
|
|
||||||
if conditions:
|
if conditions:
|
||||||
query += ' and ' + ' and '.join(conditions)
|
query += ' AND ' + ' AND '.join(conditions)
|
||||||
|
|
||||||
if filter_conditions:
|
if filter_conditions:
|
||||||
query += filter_conditions
|
query += filter_conditions
|
||||||
@@ -729,11 +744,13 @@ def add_leaves(events, start, end, filter_conditions=None):
|
|||||||
"to_date": d.to_date,
|
"to_date": d.to_date,
|
||||||
"docstatus": d.docstatus,
|
"docstatus": d.docstatus,
|
||||||
"color": d.color,
|
"color": d.color,
|
||||||
"title": cstr(d.employee_name) + (' ' + _('(Half Day)') if d.half_day else ''),
|
"all_day": int(not d.half_day),
|
||||||
|
"title": cstr(d.employee_name) + f' ({cstr(d.leave_type)})' + (' ' + _('(Half Day)') if d.half_day else ''),
|
||||||
}
|
}
|
||||||
if e not in events:
|
if e not in events:
|
||||||
events.append(e)
|
events.append(e)
|
||||||
|
|
||||||
|
|
||||||
def add_block_dates(events, start, end, employee, company):
|
def add_block_dates(events, start, end, employee, company):
|
||||||
# block days
|
# block days
|
||||||
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
|
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ frappe.views.calendar["Leave Application"] = {
|
|||||||
"end": "to_date",
|
"end": "to_date",
|
||||||
"id": "name",
|
"id": "name",
|
||||||
"title": "title",
|
"title": "title",
|
||||||
"docstatus": 1
|
"docstatus": 1,
|
||||||
|
"color": "color",
|
||||||
|
"allDay": "all_day"
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
header: {
|
header: {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class LeaveControlPanel(Document):
|
|||||||
la.docstatus = 1
|
la.docstatus = 1
|
||||||
la.save()
|
la.save()
|
||||||
leave_allocated_for.append(d[0])
|
leave_allocated_for.append(d[0])
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
if leave_allocated_for:
|
if leave_allocated_for:
|
||||||
msgprint(_("Leaves Allocated Successfully for {0}").format(comma_and(leave_allocated_for)))
|
msgprint(_("Leaves Allocated Successfully for {0}").format(comma_and(leave_allocated_for)))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import getdate, cstr, add_days, date_diff, getdate, ceil
|
from frappe.utils import getdate, cstr, add_days, date_diff, ceil
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.utils import validate_overlap
|
from erpnext.hr.utils import validate_overlap
|
||||||
from frappe.utils.background_jobs import enqueue
|
from frappe.utils.background_jobs import enqueue
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) {
|
|||||||
} else {
|
} else {
|
||||||
organizational_chart = new erpnext.HierarchyChart('Employee', wrapper, method);
|
organizational_chart = new erpnext.HierarchyChart('Employee', wrapper, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frappe.breadcrumbs.add('HR');
|
||||||
organizational_chart.show();
|
organizational_chart.show();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ def get_data(filters, leave_types):
|
|||||||
for leave_type in leave_types:
|
for leave_type in leave_types:
|
||||||
remaining = 0
|
remaining = 0
|
||||||
if leave_type in available_leave["leave_allocation"]:
|
if leave_type in available_leave["leave_allocation"]:
|
||||||
# opening balance
|
# opening balance
|
||||||
remaining = available_leave["leave_allocation"][leave_type]['remaining_leaves']
|
remaining = available_leave["leave_allocation"][leave_type]['remaining_leaves']
|
||||||
|
|
||||||
row += [remaining]
|
row += [remaining]
|
||||||
|
|||||||
@@ -96,8 +96,6 @@ def get_columns():
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return columns
|
|
||||||
|
|
||||||
|
|
||||||
def get_vehicle_log_data(filters):
|
def get_vehicle_log_data(filters):
|
||||||
start_date, end_date = get_period_dates(filters)
|
start_date, end_date = get_period_dates(filters)
|
||||||
|
|||||||
@@ -450,9 +450,9 @@ def get_sal_slip_total_benefit_given(employee, payroll_period, component=False):
|
|||||||
|
|
||||||
def get_holiday_dates_for_employee(employee, start_date, end_date):
|
def get_holiday_dates_for_employee(employee, start_date, end_date):
|
||||||
"""return a list of holiday dates for the given employee between start_date and end_date"""
|
"""return a list of holiday dates for the given employee between start_date and end_date"""
|
||||||
# return only date
|
# return only date
|
||||||
holidays = get_holidays_for_employee(employee, start_date, end_date)
|
holidays = get_holidays_for_employee(employee, start_date, end_date)
|
||||||
|
|
||||||
return [cstr(h.holiday_date) for h in holidays]
|
return [cstr(h.holiday_date) for h in holidays]
|
||||||
|
|
||||||
|
|
||||||
@@ -465,7 +465,7 @@ def get_holidays_for_employee(employee, start_date, end_date, raise_exception=Tr
|
|||||||
`raise_exception` (bool)
|
`raise_exception` (bool)
|
||||||
`only_non_weekly` (bool)
|
`only_non_weekly` (bool)
|
||||||
|
|
||||||
return: list of dicts with `holiday_date` and `description`
|
return: list of dicts with `holiday_date` and `description`
|
||||||
"""
|
"""
|
||||||
holiday_list = get_holiday_list_for_employee(employee, raise_exception=raise_exception)
|
holiday_list = get_holiday_list_for_employee(employee, raise_exception=raise_exception)
|
||||||
|
|
||||||
@@ -481,11 +481,11 @@ def get_holidays_for_employee(employee, start_date, end_date, raise_exception=Tr
|
|||||||
filters['weekly_off'] = False
|
filters['weekly_off'] = False
|
||||||
|
|
||||||
holidays = frappe.get_all(
|
holidays = frappe.get_all(
|
||||||
'Holiday',
|
'Holiday',
|
||||||
fields=['description', 'holiday_date'],
|
fields=['description', 'holiday_date'],
|
||||||
filters=filters
|
filters=filters
|
||||||
)
|
)
|
||||||
|
|
||||||
return holidays
|
return holidays
|
||||||
|
|
||||||
@erpnext.allow_regional
|
@erpnext.allow_regional
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import frappe, erpnext
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import (nowdate, getdate, now_datetime, get_datetime, flt, date_diff, get_last_day, cint,
|
from frappe.utils import (nowdate, getdate, now_datetime, get_datetime, flt, date_diff, get_last_day, cint,
|
||||||
get_first_day, get_datetime, add_days)
|
get_first_day, add_days)
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ from __future__ import unicode_literals
|
|||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
import json
|
import json
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt, getdate, cint
|
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import date_diff, add_days, getdate, add_months, get_first_day, get_datetime
|
from frappe.utils import flt, cint, date_diff, add_days, getdate, add_months, get_first_day, get_datetime
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import update_shortfall_status
|
from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import update_shortfall_status
|
||||||
@@ -107,12 +106,13 @@ class LoanRepayment(AccountsController):
|
|||||||
lia = frappe.db.get_value('Loan Interest Accrual', {'process_loan_interest_accrual':
|
lia = frappe.db.get_value('Loan Interest Accrual', {'process_loan_interest_accrual':
|
||||||
process}, ['name', 'interest_amount', 'payable_principal_amount'], as_dict=1)
|
process}, ['name', 'interest_amount', 'payable_principal_amount'], as_dict=1)
|
||||||
|
|
||||||
self.append('repayment_details', {
|
if lia:
|
||||||
'loan_interest_accrual': lia.name,
|
self.append('repayment_details', {
|
||||||
'paid_interest_amount': flt(self.total_interest_paid - self.interest_payable, precision),
|
'loan_interest_accrual': lia.name,
|
||||||
'paid_principal_amount': 0.0,
|
'paid_interest_amount': flt(self.total_interest_paid - self.interest_payable, precision),
|
||||||
'accrual_type': 'Repayment'
|
'paid_principal_amount': 0.0,
|
||||||
})
|
'accrual_type': 'Repayment'
|
||||||
|
})
|
||||||
|
|
||||||
def update_paid_amount(self):
|
def update_paid_amount(self):
|
||||||
loan = frappe.get_doc("Loan", self.against_loan)
|
loan = frappe.get_doc("Loan", self.against_loan)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ def check_for_ltv_shortfall(process_loan_security_shortfall):
|
|||||||
- flt(loan.total_principal_paid)
|
- flt(loan.total_principal_paid)
|
||||||
|
|
||||||
pledged_securities = get_pledged_security_qty(loan.name)
|
pledged_securities = get_pledged_security_qty(loan.name)
|
||||||
ltv_ratio = ''
|
ltv_ratio = 0.0
|
||||||
security_value = 0.0
|
security_value = 0.0
|
||||||
|
|
||||||
for security, qty in pledged_securities.items():
|
for security, qty in pledged_securities.items():
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class SanctionedLoanAmount(Document):
|
class SanctionedLoanAmount(Document):
|
||||||
|
|||||||
@@ -6,17 +6,17 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"item_code",
|
"item_code",
|
||||||
"item_name",
|
|
||||||
"material_request_type",
|
|
||||||
"from_warehouse",
|
"from_warehouse",
|
||||||
"warehouse",
|
"warehouse",
|
||||||
"column_break_4",
|
"item_name",
|
||||||
|
"material_request_type",
|
||||||
|
"actual_qty",
|
||||||
|
"ordered_qty",
|
||||||
"required_bom_qty",
|
"required_bom_qty",
|
||||||
|
"column_break_4",
|
||||||
"quantity",
|
"quantity",
|
||||||
"uom",
|
"uom",
|
||||||
"projected_qty",
|
"projected_qty",
|
||||||
"actual_qty",
|
|
||||||
"ordered_qty",
|
|
||||||
"reserved_qty_for_production",
|
"reserved_qty_for_production",
|
||||||
"safety_stock",
|
"safety_stock",
|
||||||
"item_details",
|
"item_details",
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"columns": 2,
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@@ -41,6 +42,7 @@
|
|||||||
"label": "Item Name"
|
"label": "Item Name"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"columns": 2,
|
||||||
"fieldname": "warehouse",
|
"fieldname": "warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@@ -50,10 +52,11 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"columns": 1,
|
||||||
"fieldname": "material_request_type",
|
"fieldname": "material_request_type",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Material Request Type",
|
"label": "Type",
|
||||||
"options": "\nPurchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided"
|
"options": "\nPurchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,10 +64,11 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"columns": 1,
|
||||||
"fieldname": "quantity",
|
"fieldname": "quantity",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Required Quantity",
|
"label": "Plan to Request Qty",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
@@ -75,11 +79,12 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"columns": 2,
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "actual_qty",
|
"fieldname": "actual_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Actual Qty",
|
"label": "Available Qty",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@@ -157,16 +162,18 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"columns": 2,
|
||||||
"fieldname": "required_bom_qty",
|
"fieldname": "required_bom_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Required Qty as per BOM",
|
"in_list_view": 1,
|
||||||
|
"label": "Qty As Per BOM",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-26 12:41:13.013149",
|
"modified": "2021-08-23 18:17:58.400462",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Material Request Plan Item",
|
"name": "Material Request Plan Item",
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
|
|
||||||
get_items_for_mr: function(frm) {
|
get_items_for_mr: function(frm) {
|
||||||
if (!frm.doc.for_warehouse) {
|
if (!frm.doc.for_warehouse) {
|
||||||
frappe.throw(__("Select warehouse for material requests"));
|
frappe.throw(__("To make material requests, 'Make Material Request for Warehouse' field is mandatory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.ignore_existing_ordered_qty) {
|
if (frm.doc.ignore_existing_ordered_qty) {
|
||||||
@@ -265,9 +265,18 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
title: title,
|
title: title,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
"fieldtype": "Table MultiSelect", "label": __("Source Warehouses (Optional)"),
|
'label': __('Target Warehouse'),
|
||||||
"fieldname": "warehouses", "options": "Production Plan Material Request Warehouse",
|
'fieldtype': 'Link',
|
||||||
"description": __("System will pickup the materials from the selected warehouses. If not specified, system will create material request for purchase."),
|
'fieldname': 'target_warehouse',
|
||||||
|
'read_only': true,
|
||||||
|
'default': frm.doc.for_warehouse
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': __('Source Warehouses (Optional)'),
|
||||||
|
'fieldtype': 'Table MultiSelect',
|
||||||
|
'fieldname': 'warehouses',
|
||||||
|
'options': 'Production Plan Material Request Warehouse',
|
||||||
|
'description': __('If source warehouse selected then system will create the material request with type Material Transfer from Source to Target warehouse. If not selected then will create the material request with type Purchase for the target warehouse.'),
|
||||||
get_query: function () {
|
get_query: function () {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
@@ -342,7 +351,11 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
|
|
||||||
frappe.prompt(fields, (row) => {
|
frappe.prompt(fields, (row) => {
|
||||||
let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials';
|
let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials';
|
||||||
open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc, warehouses: row.warehouses });
|
open_url_post(frappe.request.url, {
|
||||||
|
cmd: get_template_url,
|
||||||
|
doc: frm.doc,
|
||||||
|
warehouses: row.warehouses
|
||||||
|
});
|
||||||
}, __('Select Warehouses to get Stock for Materials Planning'), __('Get Stock'));
|
}, __('Select Warehouses to get Stock for Materials Planning'), __('Get Stock'));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -300,7 +300,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "for_warehouse",
|
"fieldname": "for_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Material Request Warehouse",
|
"label": "Make Material Request for Warehouse",
|
||||||
"options": "Warehouse"
|
"options": "Warehouse"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -364,7 +364,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-06-28 20:00:33.905114",
|
"modified": "2021-08-23 17:26:03.799876",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Plan",
|
"name": "Production Plan",
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ class ProductionPlan(Document):
|
|||||||
def get_production_items(self):
|
def get_production_items(self):
|
||||||
item_dict = {}
|
item_dict = {}
|
||||||
for d in self.po_items:
|
for d in self.po_items:
|
||||||
item_details= {
|
item_details = {
|
||||||
"production_item" : d.item_code,
|
"production_item" : d.item_code,
|
||||||
"use_multi_level_bom" : d.include_exploded_items,
|
"use_multi_level_bom" : d.include_exploded_items,
|
||||||
"sales_order" : d.sales_order,
|
"sales_order" : d.sales_order,
|
||||||
@@ -346,8 +346,7 @@ class ProductionPlan(Document):
|
|||||||
"production_plan" : self.name,
|
"production_plan" : self.name,
|
||||||
"production_plan_item" : d.name,
|
"production_plan_item" : d.name,
|
||||||
"product_bundle_item" : d.product_bundle_item,
|
"product_bundle_item" : d.product_bundle_item,
|
||||||
"planned_start_date" : d.planned_start_date,
|
"planned_start_date" : d.planned_start_date
|
||||||
"make_work_order_for_sub_assembly_items": d.get("make_work_order_for_sub_assembly_items", 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item_details.update({
|
item_details.update({
|
||||||
@@ -458,6 +457,7 @@ class ProductionPlan(Document):
|
|||||||
warehouse = get_default_warehouse()
|
warehouse = get_default_warehouse()
|
||||||
wo = frappe.new_doc("Work Order")
|
wo = frappe.new_doc("Work Order")
|
||||||
wo.update(item)
|
wo.update(item)
|
||||||
|
wo.planned_start_date = item.get('planned_start_date') or item.get('schedule_date')
|
||||||
|
|
||||||
if item.get("warehouse"):
|
if item.get("warehouse"):
|
||||||
wo.fg_warehouse = item.get("warehouse")
|
wo.fg_warehouse = item.get("warehouse")
|
||||||
@@ -569,7 +569,10 @@ def download_raw_materials(doc, warehouses=None):
|
|||||||
'Reserved Qty for Production', 'Safety Stock', 'Required Qty']]
|
'Reserved Qty for Production', 'Safety Stock', 'Required Qty']]
|
||||||
|
|
||||||
doc.warehouse = None
|
doc.warehouse = None
|
||||||
for d in get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True):
|
frappe.flags.show_qty_in_stock_uom = 1
|
||||||
|
items = get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True)
|
||||||
|
|
||||||
|
for d in items:
|
||||||
item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('warehouse'),
|
item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('warehouse'),
|
||||||
d.get('required_bom_qty'), d.get('projected_qty'), d.get('actual_qty'), d.get('ordered_qty'),
|
d.get('required_bom_qty'), d.get('projected_qty'), d.get('actual_qty'), d.get('ordered_qty'),
|
||||||
d.get('planned_qty'), d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')])
|
d.get('planned_qty'), d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')])
|
||||||
@@ -605,9 +608,16 @@ def get_exploded_items(item_details, company, bom_no, include_non_stock_items, p
|
|||||||
and bom.name=%s and item.is_stock_item in (1, {0})
|
and bom.name=%s and item.is_stock_item in (1, {0})
|
||||||
group by bei.item_code, bei.stock_uom""".format(0 if include_non_stock_items else 1),
|
group by bei.item_code, bei.stock_uom""".format(0 if include_non_stock_items else 1),
|
||||||
(planned_qty, company, bom_no), as_dict=1):
|
(planned_qty, company, bom_no), as_dict=1):
|
||||||
item_details.setdefault(d.get('item_code'), d)
|
if not d.conversion_factor and d.purchase_uom:
|
||||||
|
d.conversion_factor = get_uom_conversion_factor(d.item_code, d.purchase_uom)
|
||||||
|
item_details.setdefault(d.get('item_code'), d)
|
||||||
|
|
||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
|
def get_uom_conversion_factor(item_code, uom):
|
||||||
|
return frappe.db.get_value('UOM Conversion Detail',
|
||||||
|
{'parent': item_code, 'uom': uom}, 'conversion_factor')
|
||||||
|
|
||||||
def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_items,
|
def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_items,
|
||||||
include_subcontracted_items, parent_qty, planned_qty=1):
|
include_subcontracted_items, parent_qty, planned_qty=1):
|
||||||
items = frappe.db.sql("""
|
items = frappe.db.sql("""
|
||||||
@@ -642,6 +652,9 @@ def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_ite
|
|||||||
if d.item_code in item_details:
|
if d.item_code in item_details:
|
||||||
item_details[d.item_code].qty = item_details[d.item_code].qty + d.qty
|
item_details[d.item_code].qty = item_details[d.item_code].qty + d.qty
|
||||||
else:
|
else:
|
||||||
|
if not d.conversion_factor and d.purchase_uom:
|
||||||
|
d.conversion_factor = get_uom_conversion_factor(d.item_code, d.purchase_uom)
|
||||||
|
|
||||||
item_details[d.item_code] = d
|
item_details[d.item_code] = d
|
||||||
|
|
||||||
if data.get('include_exploded_items') and d.default_bom:
|
if data.get('include_exploded_items') and d.default_bom:
|
||||||
@@ -669,10 +682,11 @@ def get_material_request_items(row, sales_order, company,
|
|||||||
row['purchase_uom'] = row['stock_uom']
|
row['purchase_uom'] = row['stock_uom']
|
||||||
|
|
||||||
if row['purchase_uom'] != row['stock_uom']:
|
if row['purchase_uom'] != row['stock_uom']:
|
||||||
if not row['conversion_factor']:
|
if not (row['conversion_factor'] or frappe.flags.show_qty_in_stock_uom):
|
||||||
frappe.throw(_("UOM Conversion factor ({0} -> {1}) not found for item: {2}")
|
frappe.throw(_("UOM Conversion factor ({0} -> {1}) not found for item: {2}")
|
||||||
.format(row['purchase_uom'], row['stock_uom'], row.item_code))
|
.format(row['purchase_uom'], row['stock_uom'], row.item_code))
|
||||||
required_qty = required_qty / row['conversion_factor']
|
|
||||||
|
required_qty = required_qty / row['conversion_factor']
|
||||||
|
|
||||||
if frappe.db.get_value("UOM", row['purchase_uom'], "must_be_whole_number"):
|
if frappe.db.get_value("UOM", row['purchase_uom'], "must_be_whole_number"):
|
||||||
required_qty = ceil(required_qty)
|
required_qty = ceil(required_qty)
|
||||||
@@ -841,10 +855,8 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
|||||||
elif data.get('item_code'):
|
elif data.get('item_code'):
|
||||||
item_master = frappe.get_doc('Item', data['item_code']).as_dict()
|
item_master = frappe.get_doc('Item', data['item_code']).as_dict()
|
||||||
purchase_uom = item_master.purchase_uom or item_master.stock_uom
|
purchase_uom = item_master.purchase_uom or item_master.stock_uom
|
||||||
conversion_factor = 0
|
conversion_factor = (get_uom_conversion_factor(item_master.name, purchase_uom)
|
||||||
for d in item_master.get("uoms"):
|
if item_master.purchase_uom else 1.0)
|
||||||
if d.uom == purchase_uom:
|
|
||||||
conversion_factor = d.conversion_factor
|
|
||||||
|
|
||||||
item_details[item_master.name] = frappe._dict(
|
item_details[item_master.name] = frappe._dict(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from frappe import _
|
|||||||
from frappe.utils.data import comma_and
|
from frappe.utils.data import comma_and
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
# if not filters: filters = {}
|
# if not filters: filters = {}
|
||||||
columns = get_columns()
|
columns = get_columns()
|
||||||
summ_data = []
|
summ_data = []
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ def get_member_based_on_subscription(subscription_id, email=None, customer_id=No
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return frappe.get_doc("Member", members[0]["name"])
|
return frappe.get_doc("Member", members[0]["name"])
|
||||||
except:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@ def notify_failure(log):
|
|||||||
""".format(get_link_to_form("Error Log", log.name))
|
""".format(get_link_to_form("Error Log", log.name))
|
||||||
|
|
||||||
sendmail_to_system_managers("[Important] [ERPNext] Razorpay membership webhook failed , please check.", content)
|
sendmail_to_system_managers("[Important] [ERPNext] Razorpay membership webhook failed , please check.", content)
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -402,7 +402,7 @@ def get_plan_from_razorpay_id(plan_id):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return plan[0]["name"]
|
return plan[0]["name"]
|
||||||
except:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ def execute():
|
|||||||
buying_cost_center, selling_cost_center, expense_account, income_account, default_supplier
|
buying_cost_center, selling_cost_center, expense_account, income_account, default_supplier
|
||||||
FROM `tabItem`;
|
FROM `tabItem`;
|
||||||
''', companies[0].name)
|
''', companies[0].name)
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
item_details = frappe.db.sql(""" SELECT name, default_warehouse,
|
item_details = frappe.db.sql(""" SELECT name, default_warehouse,
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ def execute():
|
|||||||
|
|
||||||
frappe.reload_doc("stock", "doctype", "stock_ledger_entry")
|
frappe.reload_doc("stock", "doctype", "stock_ledger_entry")
|
||||||
frappe.reload_doc("stock", "doctype", "serial_no")
|
frappe.reload_doc("stock", "doctype", "serial_no")
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ def execute():
|
|||||||
try:
|
try:
|
||||||
employee_other_income.submit()
|
employee_other_income.submit()
|
||||||
migrated.append([proof.employee, proof.payroll_period])
|
migrated.append([proof.employee, proof.payroll_period])
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if not frappe.db.table_exists("Employee Tax Exemption Declaration"):
|
if not frappe.db.table_exists("Employee Tax Exemption Declaration"):
|
||||||
@@ -108,5 +108,5 @@ def execute():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
employee_other_income.submit()
|
employee_other_income.submit()
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -7,17 +7,17 @@ def execute():
|
|||||||
try:
|
try:
|
||||||
# Rename the field
|
# Rename the field
|
||||||
rename_field('HR Settings', 'stop_birthday_reminders', 'send_birthday_reminders')
|
rename_field('HR Settings', 'stop_birthday_reminders', 'send_birthday_reminders')
|
||||||
|
|
||||||
# Reverse the value
|
# Reverse the value
|
||||||
old_value = frappe.db.get_single_value('HR Settings', 'send_birthday_reminders')
|
old_value = frappe.db.get_single_value('HR Settings', 'send_birthday_reminders')
|
||||||
|
|
||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
'HR Settings',
|
'HR Settings',
|
||||||
'HR Settings',
|
'HR Settings',
|
||||||
'send_birthday_reminders',
|
'send_birthday_reminders',
|
||||||
1 if old_value == 0 else 0
|
1 if old_value == 0 else 0
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if e.args[0] != 1054:
|
if e.args[0] != 1054:
|
||||||
raise
|
raise
|
||||||
@@ -35,10 +35,10 @@ def get_reconciled_bank_transactions(intra_company_pe):
|
|||||||
|
|
||||||
for payment_entry in intra_company_pe:
|
for payment_entry in intra_company_pe:
|
||||||
reconciled_bank_transactions[payment_entry] = frappe.get_all(
|
reconciled_bank_transactions[payment_entry] = frappe.get_all(
|
||||||
'Bank Transaction Payments',
|
'Bank Transaction Payments',
|
||||||
filters = {
|
filters = {
|
||||||
'payment_entry': payment_entry
|
'payment_entry': payment_entry
|
||||||
},
|
},
|
||||||
pluck='parent'
|
pluck='parent'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model import data_field_options
|
from frappe.model import data_field_options
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
|
|
||||||
for field in frappe.get_all('Custom Field',
|
for field in frappe.get_all('Custom Field',
|
||||||
fields = ['name'],
|
fields = ['name'],
|
||||||
filters = {
|
filters = {
|
||||||
'fieldtype': 'Data',
|
'fieldtype': 'Data',
|
||||||
@@ -16,7 +16,7 @@ def execute():
|
|||||||
|
|
||||||
if field not in data_field_options:
|
if field not in data_field_options:
|
||||||
frappe.db.sql("""
|
frappe.db.sql("""
|
||||||
UPDATE
|
UPDATE
|
||||||
`tabCustom Field`
|
`tabCustom Field`
|
||||||
SET
|
SET
|
||||||
options=NULL
|
options=NULL
|
||||||
|
|||||||
@@ -20,5 +20,5 @@ def execute():
|
|||||||
})
|
})
|
||||||
if count % 200 == 0:
|
if count % 200 == 0:
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except:
|
except Exception:
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
|
|||||||
@@ -242,7 +242,11 @@ def get_salary_structure(employee):
|
|||||||
order_by = "from_date desc")[0].salary_structure
|
order_by = "from_date desc")[0].salary_structure
|
||||||
|
|
||||||
def get_last_salary_slip(employee):
|
def get_last_salary_slip(employee):
|
||||||
return frappe.get_list("Salary Slip", filters = {
|
salary_slips = frappe.get_list("Salary Slip", filters = {
|
||||||
"employee": employee, 'docstatus': 1
|
"employee": employee, 'docstatus': 1
|
||||||
},
|
},
|
||||||
order_by = "start_date desc")[0].name
|
order_by = "start_date desc"
|
||||||
|
)
|
||||||
|
if not salary_slips:
|
||||||
|
return
|
||||||
|
return salary_slips[0].name
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ class TestGratuity(unittest.TestCase):
|
|||||||
frappe.db.sql("DELETE FROM `tabGratuity`")
|
frappe.db.sql("DELETE FROM `tabGratuity`")
|
||||||
frappe.db.sql("DELETE FROM `tabAdditional Salary` WHERE ref_doctype = 'Gratuity'")
|
frappe.db.sql("DELETE FROM `tabAdditional Salary` WHERE ref_doctype = 'Gratuity'")
|
||||||
|
|
||||||
|
def test_get_last_salary_slip_should_return_none_for_new_employee(self):
|
||||||
|
new_employee = make_employee("new_employee@salary.com", company='_Test Company')
|
||||||
|
salary_slip = get_last_salary_slip(new_employee)
|
||||||
|
assert salary_slip is None
|
||||||
|
|
||||||
def test_check_gratuity_amount_based_on_current_slab_and_additional_salary_creation(self):
|
def test_check_gratuity_amount_based_on_current_slab_and_additional_salary_creation(self):
|
||||||
employee, sal_slip = create_employee_and_get_last_salary_slip()
|
employee, sal_slip = create_employee_and_get_last_salary_slip()
|
||||||
|
|
||||||
|
|||||||
@@ -135,15 +135,15 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
|
|
||||||
change_form_labels: function(frm, company_currency) {
|
change_form_labels: function(frm, company_currency) {
|
||||||
frm.set_currency_labels(["base_hour_rate", "base_gross_pay", "base_total_deduction",
|
frm.set_currency_labels(["base_hour_rate", "base_gross_pay", "base_total_deduction",
|
||||||
"base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"],
|
"base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "gross_base_year_to_date"],
|
||||||
company_currency);
|
company_currency);
|
||||||
|
|
||||||
frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date"],
|
frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date", "gross_year_to_date"],
|
||||||
frm.doc.currency);
|
frm.doc.currency);
|
||||||
|
|
||||||
// toggle fields
|
// toggle fields
|
||||||
frm.toggle_display(["exchange_rate", "base_hour_rate", "base_gross_pay", "base_total_deduction",
|
frm.toggle_display(["exchange_rate", "base_hour_rate", "base_gross_pay", "base_total_deduction",
|
||||||
"base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date"],
|
"base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "base_gross_year_to_date"],
|
||||||
frm.doc.currency != company_currency);
|
frm.doc.currency != company_currency);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"totals",
|
"totals",
|
||||||
"gross_pay",
|
"gross_pay",
|
||||||
"base_gross_pay",
|
"base_gross_pay",
|
||||||
|
"gross_year_to_date",
|
||||||
|
"base_gross_year_to_date",
|
||||||
"column_break_25",
|
"column_break_25",
|
||||||
"total_deduction",
|
"total_deduction",
|
||||||
"base_total_deduction",
|
"base_total_deduction",
|
||||||
@@ -625,13 +627,27 @@
|
|||||||
"label": "Leave Details",
|
"label": "Leave Details",
|
||||||
"options": "Salary Slip Leave",
|
"options": "Salary Slip Leave",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "gross_year_to_date",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Gross Year To Date",
|
||||||
|
"options": "currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "base_gross_year_to_date",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Gross Year To Date(Company Currency)",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 9,
|
"idx": 9,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-31 22:44:09.772331",
|
"modified": "2021-09-01 10:22:52.374549",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
|||||||
@@ -1218,7 +1218,7 @@ class SalarySlip(TransactionBase):
|
|||||||
period_start_date, period_end_date = self.get_year_to_date_period()
|
period_start_date, period_end_date = self.get_year_to_date_period()
|
||||||
|
|
||||||
salary_slip_sum = frappe.get_list('Salary Slip',
|
salary_slip_sum = frappe.get_list('Salary Slip',
|
||||||
fields = ['sum(net_pay) as sum'],
|
fields = ['sum(net_pay) as net_sum', 'sum(gross_pay) as gross_sum'],
|
||||||
filters = {'employee_name' : self.employee_name,
|
filters = {'employee_name' : self.employee_name,
|
||||||
'start_date' : ['>=', period_start_date],
|
'start_date' : ['>=', period_start_date],
|
||||||
'end_date' : ['<', period_end_date],
|
'end_date' : ['<', period_end_date],
|
||||||
@@ -1226,10 +1226,13 @@ class SalarySlip(TransactionBase):
|
|||||||
'docstatus': 1
|
'docstatus': 1
|
||||||
})
|
})
|
||||||
|
|
||||||
year_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0
|
year_to_date = flt(salary_slip_sum[0].net_sum) if salary_slip_sum else 0.0
|
||||||
|
gross_year_to_date = flt(salary_slip_sum[0].gross_sum) if salary_slip_sum else 0.0
|
||||||
|
|
||||||
year_to_date += self.net_pay
|
year_to_date += self.net_pay
|
||||||
|
gross_year_to_date += self.gross_pay
|
||||||
self.year_to_date = year_to_date
|
self.year_to_date = year_to_date
|
||||||
|
self.gross_year_to_date = gross_year_to_date
|
||||||
|
|
||||||
def compute_month_to_date(self):
|
def compute_month_to_date(self):
|
||||||
month_to_date = 0
|
month_to_date = 0
|
||||||
|
|||||||
@@ -846,21 +846,25 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
|
|
||||||
if (frappe.meta.get_docfield(this.frm.doctype, "shipping_address") &&
|
if (frappe.meta.get_docfield(this.frm.doctype, "shipping_address") &&
|
||||||
in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
|
in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
|
||||||
erpnext.utils.get_shipping_address(this.frm, function(){
|
erpnext.utils.get_shipping_address(this.frm, function() {
|
||||||
set_party_account(set_pricing);
|
set_party_account(set_pricing);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get default company billing address in Purchase Invoice, Order and Receipt
|
// Get default company billing address in Purchase Invoice, Order and Receipt
|
||||||
frappe.call({
|
if (this.frm.doc.company && frappe.meta.get_docfield(this.frm.doctype, "billing_address")) {
|
||||||
'method': 'frappe.contacts.doctype.address.address.get_default_address',
|
frappe.call({
|
||||||
'args': {
|
method: "erpnext.setup.doctype.company.company.get_default_company_address",
|
||||||
'doctype': 'Company',
|
args: {name: this.frm.doc.company, existing_address: this.frm.doc.billing_address || ""},
|
||||||
'name': this.frm.doc.company
|
debounce: 2000,
|
||||||
},
|
callback: function(r) {
|
||||||
'callback': function(r) {
|
if (r.message) {
|
||||||
me.frm.set_value('billing_address', r.message);
|
me.frm.set_value("billing_address", r.message);
|
||||||
}
|
} else {
|
||||||
});
|
me.frm.set_value("company_address", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
set_party_account(set_pricing);
|
set_party_account(set_pricing);
|
||||||
|
|||||||
@@ -67,8 +67,6 @@ erpnext.HierarchyChart = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
frappe.breadcrumbs.add('HR');
|
|
||||||
|
|
||||||
this.setup_actions();
|
this.setup_actions();
|
||||||
if ($(`[data-fieldname="company"]`).length) return;
|
if ($(`[data-fieldname="company"]`).length) return;
|
||||||
let me = this;
|
let me = this;
|
||||||
@@ -83,8 +81,9 @@ erpnext.HierarchyChart = class {
|
|||||||
reqd: 1,
|
reqd: 1,
|
||||||
change: () => {
|
change: () => {
|
||||||
me.company = undefined;
|
me.company = undefined;
|
||||||
|
$('#hierarchy-chart-wrapper').remove();
|
||||||
|
|
||||||
if (company.get_value() && me.company != company.get_value()) {
|
if (company.get_value()) {
|
||||||
me.company = company.get_value();
|
me.company = company.get_value();
|
||||||
|
|
||||||
// svg for connectors
|
// svg for connectors
|
||||||
@@ -92,6 +91,8 @@ erpnext.HierarchyChart = class {
|
|||||||
me.setup_hierarchy();
|
me.setup_hierarchy();
|
||||||
me.render_root_nodes();
|
me.render_root_nodes();
|
||||||
me.all_nodes_expanded = false;
|
me.all_nodes_expanded = false;
|
||||||
|
} else {
|
||||||
|
frappe.throw(__('Please select a company first.'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -172,11 +173,11 @@ erpnext.HierarchyChart = class {
|
|||||||
</ul>`);
|
</ul>`);
|
||||||
|
|
||||||
this.page.main
|
this.page.main
|
||||||
.find('#hierarchy-chart-wrapper')
|
.find('#hierarchy-chart')
|
||||||
|
.empty()
|
||||||
.append(this.$hierarchy);
|
.append(this.$hierarchy);
|
||||||
|
|
||||||
this.nodes = {};
|
this.nodes = {};
|
||||||
this.all_nodes_expanded = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
make_svg_markers() {
|
make_svg_markers() {
|
||||||
@@ -203,6 +204,8 @@ erpnext.HierarchyChart = class {
|
|||||||
<g id="connectors" fill="none">
|
<g id="connectors" fill="none">
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
<div id="hierarchy-chart">
|
||||||
|
</div>
|
||||||
</div>`);
|
</div>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +222,10 @@ erpnext.HierarchyChart = class {
|
|||||||
let expand_node = undefined;
|
let expand_node = undefined;
|
||||||
let node = undefined;
|
let node = undefined;
|
||||||
|
|
||||||
$.each(r.message, (i, data) => {
|
$.each(r.message, (_i, data) => {
|
||||||
|
if ($(`#${data.id}`).length)
|
||||||
|
return;
|
||||||
|
|
||||||
node = new me.Node({
|
node = new me.Node({
|
||||||
id: data.id,
|
id: data.id,
|
||||||
parent: $('<li class="child-node"></li>').appendTo(me.$hierarchy.find('.node-children')),
|
parent: $('<li class="child-node"></li>').appendTo(me.$hierarchy.find('.node-children')),
|
||||||
@@ -290,7 +296,7 @@ erpnext.HierarchyChart = class {
|
|||||||
() => frappe.dom.freeze(),
|
() => frappe.dom.freeze(),
|
||||||
() => this.setup_hierarchy(),
|
() => this.setup_hierarchy(),
|
||||||
() => this.render_root_nodes(true),
|
() => this.render_root_nodes(true),
|
||||||
() => this.get_all_nodes(node.id, node.name),
|
() => this.get_all_nodes(),
|
||||||
(data_list) => this.render_children_of_all_nodes(data_list),
|
(data_list) => this.render_children_of_all_nodes(data_list),
|
||||||
() => frappe.dom.unfreeze()
|
() => frappe.dom.unfreeze()
|
||||||
]);
|
]);
|
||||||
@@ -341,15 +347,13 @@ erpnext.HierarchyChart = class {
|
|||||||
node.expanded = true;
|
node.expanded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_all_nodes(node_id, node_name) {
|
get_all_nodes() {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.utilities.hierarchy_chart.get_all_nodes',
|
method: 'erpnext.utilities.hierarchy_chart.get_all_nodes',
|
||||||
args: {
|
args: {
|
||||||
method: this.method,
|
method: this.method,
|
||||||
company: this.company,
|
company: this.company
|
||||||
parent: node_id,
|
|
||||||
parent_name: node_name
|
|
||||||
},
|
},
|
||||||
callback: (r) => {
|
callback: (r) => {
|
||||||
resolve(r.message);
|
resolve(r.message);
|
||||||
|
|||||||
@@ -59,8 +59,6 @@ erpnext.HierarchyChartMobile = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
frappe.breadcrumbs.add('HR');
|
|
||||||
|
|
||||||
let me = this;
|
let me = this;
|
||||||
if ($(`[data-fieldname="company"]`).length) return;
|
if ($(`[data-fieldname="company"]`).length) return;
|
||||||
|
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ $.extend(erpnext, {
|
|||||||
|
|
||||||
proceed_save_with_reminders_frequency_change: () => {
|
proceed_save_with_reminders_frequency_change: () => {
|
||||||
frappe.ui.hide_open_dialog();
|
frappe.ui.hide_open_dialog();
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.hr.doctype.hr_settings.hr_settings.set_proceed_with_frequency_change',
|
method: 'erpnext.hr.doctype.hr_settings.hr_settings.set_proceed_with_frequency_change',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
cur_frm.save();
|
cur_frm.save();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -289,8 +289,8 @@ erpnext.utils.get_shipping_address = function(frm, callback) {
|
|||||||
company: frm.doc.company,
|
company: frm.doc.company,
|
||||||
address: frm.doc.shipping_address
|
address: frm.doc.shipping_address
|
||||||
},
|
},
|
||||||
callback: function(r){
|
callback: function(r) {
|
||||||
if (r.message){
|
if (r.message) {
|
||||||
frm.set_value("shipping_address", r.message[0]) //Address title or name
|
frm.set_value("shipping_address", r.message[0]) //Address title or name
|
||||||
frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page
|
frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ def get_transactions(filters, as_dict=1):
|
|||||||
def run(params_method, filters):
|
def run(params_method, filters):
|
||||||
extra_fields, extra_joins, extra_filters = params_method(filters)
|
extra_fields, extra_joins, extra_filters = params_method(filters)
|
||||||
return run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=as_dict)
|
return run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=as_dict)
|
||||||
|
|
||||||
def sort_by(row):
|
def sort_by(row):
|
||||||
# "Belegdatum" is in the fifth column when list format is used
|
# "Belegdatum" is in the fifth column when list format is used
|
||||||
return row["Belegdatum" if as_dict else 5]
|
return row["Belegdatum" if as_dict else 5]
|
||||||
@@ -361,7 +361,7 @@ def run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=1):
|
|||||||
FROM `tabGL Entry` gl
|
FROM `tabGL Entry` gl
|
||||||
|
|
||||||
/* Kontonummer */
|
/* Kontonummer */
|
||||||
LEFT JOIN `tabAccount` acc
|
LEFT JOIN `tabAccount` acc
|
||||||
ON gl.account = acc.name
|
ON gl.account = acc.name
|
||||||
|
|
||||||
LEFT JOIN `tabParty Account` par
|
LEFT JOIN `tabParty Account` par
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def get_data(filters):
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Regular expression set to remove all the special characters
|
# Regular expression set to remove all the special characters
|
||||||
special_characters = "[$%^*()+\\[\]{};':\"\\|<>.?]"
|
special_characters = r"[$%^*()+\\[\]{};':\"\\|<>.?]"
|
||||||
|
|
||||||
for row in data:
|
for row in data:
|
||||||
set_defaults(row)
|
set_defaults(row)
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ def get_result_as_list(data, filters):
|
|||||||
if d.get("voucher_no").startswith("{0}-".format(JournalCode)) or d.get("voucher_no").startswith("{0}/".format(JournalCode)):
|
if d.get("voucher_no").startswith("{0}-".format(JournalCode)) or d.get("voucher_no").startswith("{0}/".format(JournalCode)):
|
||||||
EcritureNum = re.split("-|/", d.get("voucher_no"))[1]
|
EcritureNum = re.split("-|/", d.get("voucher_no"))[1]
|
||||||
else:
|
else:
|
||||||
EcritureNum = re.search("{0}(\d+)".format(JournalCode), d.get("voucher_no"), re.IGNORECASE).group(1)
|
EcritureNum = re.search(r"{0}(\d+)".format(JournalCode), d.get("voucher_no"), re.IGNORECASE).group(1)
|
||||||
|
|
||||||
EcritureDate = format_datetime(d.get("GlPostDate"), "yyyyMMdd")
|
EcritureDate = format_datetime(d.get("GlPostDate"), "yyyyMMdd")
|
||||||
|
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ class VATAuditReport(object):
|
|||||||
row["posting_date"] = formatdate(inv_data.get("posting_date"), "dd-mm-yyyy")
|
row["posting_date"] = formatdate(inv_data.get("posting_date"), "dd-mm-yyyy")
|
||||||
row["voucher_type"] = doctype
|
row["voucher_type"] = doctype
|
||||||
row["voucher_no"] = inv
|
row["voucher_no"] = inv
|
||||||
row["party_type"] = "Customer" if doctype == "Sales Invoice" else "Supplier"
|
row["party_type"] = "Customer" if doctype == "Sales Invoice" else "Supplier"
|
||||||
row["party"] = inv_data.get("party")
|
row["party"] = inv_data.get("party")
|
||||||
row["remarks"] = inv_data.get("remarks")
|
row["remarks"] = inv_data.get("remarks")
|
||||||
row["gross_amount"]= item_details[0].get("gross_amount")
|
row["gross_amount"]= item_details[0].get("gross_amount")
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def make_custom_fields(update=True):
|
|||||||
'Sales Invoice Item': is_zero_rated,
|
'Sales Invoice Item': is_zero_rated,
|
||||||
'Purchase Invoice Item': is_zero_rated
|
'Purchase Invoice Item': is_zero_rated
|
||||||
}
|
}
|
||||||
|
|
||||||
create_custom_fields(custom_fields, update=update)
|
create_custom_fields(custom_fields, update=update)
|
||||||
|
|
||||||
def add_permissions():
|
def add_permissions():
|
||||||
@@ -36,7 +36,7 @@ def add_permissions():
|
|||||||
add_permission(doctype, role, 0)
|
add_permission(doctype, role, 0)
|
||||||
update_permission_property(doctype, role, 0, 'write', 1)
|
update_permission_property(doctype, role, 0, 'write', 1)
|
||||||
update_permission_property(doctype, role, 0, 'create', 1)
|
update_permission_property(doctype, role, 0, 'create', 1)
|
||||||
|
|
||||||
|
|
||||||
if not frappe.db.get_value('Custom Role', dict(report="VAT Audit Report")):
|
if not frappe.db.get_value('Custom Role', dict(report="VAT Audit Report")):
|
||||||
frappe.get_doc(dict(
|
frappe.get_doc(dict(
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, json
|
import json
|
||||||
|
import frappe
|
||||||
from frappe.utils.nestedset import get_root_of
|
from frappe.utils.nestedset import get_root_of
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint
|
||||||
from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups
|
from erpnext.accounts.doctype.pos_profile.pos_profile import get_item_groups
|
||||||
@@ -209,7 +210,6 @@ def check_opening_entry(user):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_opening_voucher(pos_profile, company, balance_details):
|
def create_opening_voucher(pos_profile, company, balance_details):
|
||||||
import json
|
|
||||||
balance_details = json.loads(balance_details)
|
balance_details = json.loads(balance_details)
|
||||||
|
|
||||||
new_pos_opening = frappe.get_doc({
|
new_pos_opening = frappe.get_doc({
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user