Merge develop

This commit is contained in:
Himanshu Warekar 2019-04-15 22:56:41 +05:30
commit 9b7c7787e6
20 changed files with 3386 additions and 1443 deletions

View File

@ -0,0 +1,177 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2019-03-07 12:07:09.416101",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_invoice",
"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": "Invoice",
"length": 0,
"no_copy": 0,
"options": "Sales Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sales_invoice.customer",
"fieldname": "customer",
"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": "Customer",
"length": 0,
"no_copy": 0,
"options": "Customer",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sales_invoice.posting_date",
"fieldname": "posting_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": "Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sales_invoice.grand_total",
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Outstanding Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-03-07 16:38:03.622666",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Discounted Invoice",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class DiscountedInvoice(Document):
pass

View File

@ -175,13 +175,20 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
else: else:
party_condition = "" party_condition = ""
if against_voucher_type == "Sales Invoice":
party_account = frappe.db.get_value(against_voucher_type, against_voucher, "debit_to")
account_condition = "and account in ({0}, {1})".format(frappe.db.escape(account), frappe.db.escape(party_account))
else:
account_condition = " and account = {0}".format(frappe.db.escape(account))
# get final outstanding amt # get final outstanding amt
bal = flt(frappe.db.sql(""" bal = flt(frappe.db.sql("""
select sum(debit_in_account_currency) - sum(credit_in_account_currency) select sum(debit_in_account_currency) - sum(credit_in_account_currency)
from `tabGL Entry` from `tabGL Entry`
where against_voucher_type=%s and against_voucher=%s where against_voucher_type=%s and against_voucher=%s
and account = %s {0}""".format(party_condition), and voucher_type != 'Invoice Discounting'
(against_voucher_type, against_voucher, account))[0][0] or 0.0) {0} {1}""".format(party_condition, account_condition),
(against_voucher_type, against_voucher))[0][0] or 0.0)
if against_voucher_type == 'Purchase Invoice': if against_voucher_type == 'Purchase Invoice':
bal = -bal bal = -bal

View File

@ -0,0 +1,197 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Invoice Discounting', {
setup: (frm) => {
frm.set_query("sales_invoice", "invoices", (doc) => {
return {
"filters": {
"docstatus": 1,
"company": doc.company,
"outstanding_amount": [">", 0]
},
};
});
frm.events.filter_accounts("bank_account", frm, {"account_type": "Bank"});
frm.events.filter_accounts("bank_charges_account", frm, {"root_type": "Expense"});
frm.events.filter_accounts("short_term_loan", frm, {"root_type": "Liability"});
frm.events.filter_accounts("accounts_receivable_credit", frm, {"account_type": "Receivable"});
frm.events.filter_accounts("accounts_receivable_discounted", frm, {"account_type": "Receivable"});
frm.events.filter_accounts("accounts_receivable_unpaid", frm, {"account_type": "Receivable"});
},
filter_accounts: (fieldname, frm, addl_filters) => {
let filters = {
"company": frm.doc.company,
"is_group": 0
};
if(addl_filters) Object.assign(filters, addl_filters);
frm.set_query(fieldname, () => { return { "filters": filters }; });
},
refresh: (frm) => {
frm.events.show_general_ledger(frm);
if(frm.doc.docstatus === 0) {
frm.add_custom_button(__('Get Invoices'), function() {
frm.events.get_invoices(frm);
});
}
if(frm.doc.docstatus === 1 && frm.doc.status !== "Settled") {
if(frm.doc.status == "Sanctioned") {
frm.add_custom_button(__('Disburse Loan'), function() {
frm.events.create_disbursement_entry(frm);
}).addClass("btn-primary");
}
if(frm.doc.status == "Disbursed") {
frm.add_custom_button(__('Close Loan'), function() {
frm.events.close_loan(frm);
}).addClass("btn-primary");
}
}
},
loan_start_date: (frm) => {
frm.events.set_end_date(frm);
},
loan_period: (frm) => {
frm.events.set_end_date(frm);
},
set_end_date: (frm) => {
if(frm.doc.loan_start_date && frm.doc.loan_period) {
let end_date = frappe.datetime.add_days(frm.doc.loan_start_date, frm.doc.loan_period);
frm.set_value("loan_end_date", end_date);
}
},
validate: (frm) => {
frm.events.calculate_total_amount(frm);
},
calculate_total_amount: (frm) => {
let total_amount = 0.0;
for (let row of (frm.doc.invoices || [])) {
total_amount += flt(row.outstanding_amount);
}
frm.set_value("total_amount", total_amount);
},
get_invoices: (frm) => {
var d = new frappe.ui.Dialog({
title: __('Get Invoices based on Filters'),
fields: [
{
"label": "Customer",
"fieldname": "customer",
"fieldtype": "Link",
"options": "Customer"
},
{
"label": "From Date",
"fieldname": "from_date",
"fieldtype": "Date"
},
{
"label": "To Date",
"fieldname": "to_date",
"fieldtype": "Date"
},
{
"fieldname": "col_break",
"fieldtype": "Column Break",
},
{
"label": "Min Amount",
"fieldname": "min_amount",
"fieldtype": "Currency"
},
{
"label": "Max Amount",
"fieldname": "max_amount",
"fieldtype": "Currency"
}
],
primary_action: function() {
var data = d.get_values();
frappe.call({
method: "erpnext.accounts.doctype.invoice_discounting.invoice_discounting.get_invoices",
args: {
filters: data
},
callback: function(r) {
if(!r.exc) {
d.hide();
$.each(r.message, function(i, v) {
frm.doc.invoices = frm.doc.invoices.filter(row => row.sales_invoice);
let row = frm.add_child("invoices");
$.extend(row, v);
});
refresh_field("invoices");
}
}
});
},
primary_action_label: __('Get Invocies')
});
d.show();
},
create_disbursement_entry: (frm) => {
frappe.call({
method:"create_disbursement_entry",
doc: frm.doc,
callback: function(r) {
if(!r.exc){
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
}
});
},
close_loan: (frm) => {
frappe.call({
method:"close_loan",
doc: frm.doc,
callback: function(r) {
if(!r.exc){
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
}
});
},
show_general_ledger: (frm) => {
if(frm.doc.docstatus===1) {
cur_frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
from_date: frm.doc.posting_date,
to_date: frm.doc.posting_date,
company: frm.doc.company,
group_by: "Group by Voucher (Consolidated)"
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
}
}
});
frappe.ui.form.on('Discounted Invoice', {
sales_invoice: (frm) => {
frm.events.calculate_total_amount(frm);
},
invoices_remove: (frm) => {
frm.events.calculate_total_amount(frm);
}
});

View File

@ -0,0 +1,773 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "ACC-INV-DISC-.YYYY.-.#####",
"beta": 0,
"creation": "2019-03-07 12:01:56.296952",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "posting_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": "Posting Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "loan_start_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Loan Start Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "loan_period",
"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": "Loan Period",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "loan_end_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Loan End Date",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 1,
"options": "Draft\nSanctioned\nDisbursed\nSettled\nCancelled",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"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": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "invoices",
"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": "Invoices",
"length": 0,
"no_copy": 0,
"options": "Discounted Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "total_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_charges",
"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": "Bank Charges",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "short_term_loan",
"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": "Short Term Loan 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,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_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": "Bank 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,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_charges_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": "Bank Charges 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,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_15",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounts_receivable_credit",
"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": "Accounts Receivable Credit 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,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounts_receivable_discounted",
"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": "Accounts Receivable Discounted 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,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounts_receivable_unpaid",
"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": "Accounts Receivable Unpaid 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,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Invoice Discounting",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-08 14:24:31.222027",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Invoice Discounting",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, json
from frappe import _
from frappe.utils import flt, getdate, nowdate, add_days
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import make_gl_entries
class InvoiceDiscounting(AccountsController):
def validate(self):
self.validate_mandatory()
self.calculate_total_amount()
self.set_status()
self.set_end_date()
def set_end_date(self):
if self.loan_start_date and self.loan_period:
self.loan_end_date = add_days(self.loan_start_date, self.loan_period)
def validate_mandatory(self):
if self.docstatus == 1 and not (self.loan_start_date and self.loan_period):
frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting"))
def calculate_total_amount(self):
self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices])
def on_submit(self):
self.make_gl_entries()
def on_cancel(self):
self.set_status()
self.make_gl_entries()
def set_status(self):
self.status = "Draft"
if self.docstatus == 1:
self.status = "Sanctioned"
elif self.docstatus == 2:
self.status = "Cancelled"
def make_gl_entries(self):
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
gl_entries = []
for d in self.invoices:
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice,
["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1)
if d.outstanding_amount:
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
d.precision("outstanding_amount"))
ar_credit_account_currency = frappe.get_cached_value("Account", self.accounts_receivable_credit, "currency")
gl_entries.append(self.get_gl_dict({
"account": inv.debit_to,
"party_type": "Customer",
"party": d.customer,
"against": self.accounts_receivable_credit,
"credit": outstanding_in_company_currency,
"credit_in_account_currency": outstanding_in_company_currency \
if inv.party_account_currency==company_currency else d.outstanding_amount,
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice"
}, inv.party_account_currency))
gl_entries.append(self.get_gl_dict({
"account": self.accounts_receivable_credit,
"party_type": "Customer",
"party": d.customer,
"against": inv.debit_to,
"debit": outstanding_in_company_currency,
"debit_in_account_currency": outstanding_in_company_currency \
if ar_credit_account_currency==company_currency else d.outstanding_amount,
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice"
}, ar_credit_account_currency))
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
def create_disbursement_entry(self):
je = frappe.new_doc("Journal Entry")
je.voucher_type = 'Journal Entry'
je.company = self.company
je.remark = 'Loan Disbursement entry against Invoice Discounting: ' + self.name
je.append("accounts", {
"account": self.bank_account,
"debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
})
je.append("accounts", {
"account": self.bank_charges_account,
"debit_in_account_currency": flt(self.bank_charges)
})
je.append("accounts", {
"account": self.short_term_loan,
"credit_in_account_currency": flt(self.total_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name
})
for d in self.invoices:
je.append("accounts", {
"account": self.accounts_receivable_discounted,
"debit_in_account_currency": flt(d.outstanding_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
je.append("accounts", {
"account": self.accounts_receivable_credit,
"credit_in_account_currency": flt(d.outstanding_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
return je
def close_loan(self):
je = frappe.new_doc("Journal Entry")
je.voucher_type = 'Journal Entry'
je.company = self.company
je.remark = 'Loan Settlement entry against Invoice Discounting: ' + self.name
je.append("accounts", {
"account": self.short_term_loan,
"debit_in_account_currency": flt(self.total_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
})
je.append("accounts", {
"account": self.bank_account,
"credit_in_account_currency": flt(self.total_amount)
})
if getdate(self.loan_end_date) > getdate(nowdate()):
for d in self.invoices:
je.append("accounts", {
"account": self.accounts_receivable_discounted,
"credit_in_account_currency": flt(d.outstanding_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
je.append("accounts", {
"account": self.accounts_receivable_unpaid,
"debit_in_account_currency": flt(d.outstanding_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
return je
@frappe.whitelist()
def get_invoices(filters):
filters = frappe._dict(json.loads(filters))
cond = []
if filters.customer:
cond.append("customer=%(customer)s")
if filters.from_date:
cond.append("posting_date >= %(from_date)s")
if filters.to_date:
cond.append("posting_date <= %(to_date)s")
if filters.min_amount:
cond.append("base_grand_total >= %(min_amount)s")
if filters.max_amount:
cond.append("base_grand_total <= %(max_amount)s")
where_condition = ""
if cond:
where_condition += " and " + " and ".join(cond)
return frappe.db.sql("""
select
name as sales_invoice,
customer,
posting_date,
outstanding_amount
from `tabSales Invoice`
where
docstatus = 1
and outstanding_amount > 0
%s
""" % where_condition, filters, as_dict=1)

View File

@ -0,0 +1,21 @@
frappe.listview_settings['Invoice Discounting'] = {
add_fields: ["status"],
get_indicator: function(doc)
{
if(doc.status == "Draft") {
return [__("Draft"), "red", "status,=,Draft"];
}
else if(doc.status == "Sanctioned") {
return [__("Sanctioned"), "green", "status,=,Sanctioned"];
}
else if(doc.status == "Disbursed") {
return [__("Disbursed"), "blue", "status,=,Disbursed"];
}
else if(doc.status == "Settled") {
return [__("Settled"), "orange", "status,=,Settled"];
}
else if(doc.status == "Canceled") {
return [__("Canceled"), "red", "status,=,Canceled"];
}
}
};

View File

@ -0,0 +1,269 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils import nowdate, add_days, flt
import unittest
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.journal_entry.journal_entry import get_payment_entry_against_invoice
class TestInvoiceDiscounting(unittest.TestCase):
def setUp(self):
self.ar_credit = create_account(account_name="_Test Accounts Receivable Credit", parent_account = "Accounts Receivable - _TC", company="_Test Company")
self.ar_discounted = create_account(account_name="_Test Accounts Receivable Discounted", parent_account = "Accounts Receivable - _TC", company="_Test Company")
self.ar_unpaid = create_account(account_name="_Test Accounts Receivable Unpaid", parent_account = "Accounts Receivable - _TC", company="_Test Company")
self.short_term_loan = create_account(account_name="_Test Short Term Loan", parent_account = "Source of Funds (Liabilities) - _TC", company="_Test Company")
self.bank_account = create_account(account_name="_Test Bank 2", parent_account = "Bank Accounts - _TC", company="_Test Company")
self.bank_charges_account = create_account(account_name="_Test Bank Charges Account", parent_account = "Expenses - _TC", company="_Test Company")
frappe.db.set_value("Company", "_Test Company", "default_bank_account", self.bank_account)
def test_total_amount(self):
inv1 = create_sales_invoice(rate=200)
inv2 = create_sales_invoice(rate=500)
inv_disc = create_invoice_discounting([inv1.name, inv2.name],
do_not_submit=True,
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account
)
self.assertEqual(inv_disc.total_amount, 700)
def test_gl_entries_in_base_currency(self):
inv = create_sales_invoice(rate=200)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account
)
gle = get_gl_entries("Invoice Discounting", inv_disc.name)
expected_gle = {
inv.debit_to: [0.0, 200],
self.ar_credit: [200, 0.0]
}
for i, gle in enumerate(gle):
self.assertEqual([gle.debit, gle.credit], expected_gle.get(gle.account))
def test_loan_on_submit(self):
inv = create_sales_invoice(rate=300)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
period=60
)
self.assertEqual(inv_disc.status, "Sanctioned")
self.assertEqual(inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period))
def test_on_disbursed(self):
inv = create_sales_invoice(rate=500)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
)
je = inv_disc.create_disbursement_entry()
self.assertEqual(je.accounts[0].account, self.bank_account)
self.assertEqual(je.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount) - flt(inv_disc.bank_charges))
self.assertEqual(je.accounts[1].account, self.bank_charges_account)
self.assertEqual(je.accounts[1].debit_in_account_currency, flt(inv_disc.bank_charges))
self.assertEqual(je.accounts[2].account, self.short_term_loan)
self.assertEqual(je.accounts[2].credit_in_account_currency, flt(inv_disc.total_amount))
self.assertEqual(je.accounts[3].account, self.ar_discounted)
self.assertEqual(je.accounts[3].debit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(je.accounts[4].account, self.ar_credit)
self.assertEqual(je.accounts[4].credit_in_account_currency, flt(inv.outstanding_amount))
je.posting_date = nowdate()
je.submit()
inv_disc.reload()
self.assertEqual(inv_disc.status, "Disbursed")
inv.reload()
self.assertEqual(inv.outstanding_amount, 500)
def test_on_close_after_loan_period(self):
inv = create_sales_invoice(rate=600)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
period=60
)
inv_disc.create_disbursement_entry()
je = inv_disc.close_loan()
self.assertEqual(je.accounts[0].account, self.short_term_loan)
self.assertEqual(je.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
self.assertEqual(je.accounts[1].account, self.bank_account)
self.assertEqual(je.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
self.assertEqual(je.accounts[2].account, self.ar_discounted)
self.assertEqual(je.accounts[2].credit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(je.accounts[3].account, self.ar_unpaid)
self.assertEqual(je.accounts[3].debit_in_account_currency, flt(inv.outstanding_amount))
je.posting_date = nowdate()
je.submit()
inv_disc.reload()
self.assertEqual(inv_disc.status, "Settled")
def test_on_close_before_loan_period(self):
inv = create_sales_invoice(rate=700)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=add_days(nowdate(), -80),
period=60
)
inv_disc.create_disbursement_entry()
je = inv_disc.close_loan()
je.posting_date = nowdate()
je.submit()
self.assertEqual(je.accounts[0].account, self.short_term_loan)
self.assertEqual(je.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
self.assertEqual(je.accounts[1].account, self.bank_account)
self.assertEqual(je.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
inv_disc.reload()
self.assertEqual(inv_disc.status, "Settled")
def test_make_payment_before_loan_period(self):
#it has problem
inv = create_sales_invoice(rate=700)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account
)
je = inv_disc.create_disbursement_entry()
inv_disc.reload()
je.posting_date = nowdate()
je.submit()
je_on_payment = frappe.get_doc(get_payment_entry_against_invoice("Sales Invoice", inv.name))
je_on_payment.posting_date = nowdate()
je_on_payment.cheque_no = "126981"
je_on_payment.cheque_date = nowdate()
je_on_payment.save()
je_on_payment.submit()
self.assertEqual(je_on_payment.accounts[0].account, self.ar_discounted)
self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
inv.reload()
self.assertEqual(inv.outstanding_amount, 0)
def test_make_payment_before_after_period(self):
#it has problem
inv = create_sales_invoice(rate=700)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
loan_start_date=add_days(nowdate(), -10),
period=5
)
je = inv_disc.create_disbursement_entry()
inv_disc.reload()
je.posting_date = nowdate()
je.submit()
je = inv_disc.close_loan()
inv_disc.reload()
je.posting_date = nowdate()
je.submit()
je_on_payment = frappe.get_doc(get_payment_entry_against_invoice("Sales Invoice", inv.name))
je_on_payment.posting_date = nowdate()
je_on_payment.cheque_no = "126981"
je_on_payment.cheque_date = nowdate()
je_on_payment.submit()
self.assertEqual(je_on_payment.accounts[0].account, self.ar_unpaid)
self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
inv.reload()
self.assertEqual(inv.outstanding_amount, 0)
def create_invoice_discounting(invoices, **args):
args = frappe._dict(args)
inv_disc = frappe.new_doc("Invoice Discounting")
inv_disc.posting_date = args.posting_date or nowdate()
inv_disc.company = args.company or "_Test Company"
inv_disc.bank_account = args.bank_account
inv_disc.short_term_loan = args.short_term_loan
inv_disc.accounts_receivable_credit = args.accounts_receivable_credit
inv_disc.accounts_receivable_discounted = args.accounts_receivable_discounted
inv_disc.accounts_receivable_unpaid = args.accounts_receivable_unpaid
inv_disc.short_term_loan=args.short_term_loan
inv_disc.bank_charges_account=args.bank_charges_account
inv_disc.bank_account=args.bank_account
inv_disc.loan_start_date = args.start or nowdate()
inv_disc.loan_period = args.period or 30
for d in invoices:
inv_disc.append("invoices", {
"sales_invoice": d
})
inv_disc.insert()
if not args.do_not_submit:
inv_disc.submit()
return inv_disc

View File

@ -51,6 +51,7 @@ class JournalEntry(AccountsController):
self.update_expense_claim() self.update_expense_claim()
self.update_loan() self.update_loan()
self.update_inter_company_jv() self.update_inter_company_jv()
self.update_invoice_discounting()
def get_title(self): def get_title(self):
@ -81,6 +82,18 @@ class JournalEntry(AccountsController):
frappe.db.set_value("Journal Entry", self.inter_company_journal_entry_reference,\ frappe.db.set_value("Journal Entry", self.inter_company_journal_entry_reference,\
"inter_company_journal_entry_reference", self.name) "inter_company_journal_entry_reference", self.name)
def update_invoice_discounting(self):
invoice_discounting_list = [d.reference_name for d in self.accounts if d.reference_type=="Invoice Discounting"]
for inv_disc in invoice_discounting_list:
short_term_loan_account = frappe.db.get_value("Invoice Discounting", inv_disc, "short_term_loan")
for d in self.accounts:
if d.account == short_term_loan_account and d.reference_name == inv_disc:
if d.credit > 0:
status = "Disbursed"
elif d.debit > 0:
status = "Settled"
frappe.db.set_value("Invoice Discounting", inv_disc, "status", status)
def on_cancel(self): def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
@ -246,7 +259,11 @@ class JournalEntry(AccountsController):
# check if party and account match # check if party and account match
if d.reference_type in ("Sales Invoice", "Purchase Invoice"): if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
if (against_voucher[0] != d.party or against_voucher[1] != d.account): if d.reference_type == "Sales Invoice":
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
else:
party_account = against_voucher[1]
if (against_voucher[0] != d.party or party_account != d.account):
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}") frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1], .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
d.reference_type, d.reference_name)) d.reference_type, d.reference_name))
@ -688,7 +705,7 @@ def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_cur
ref_doc = frappe.get_doc(dt, dn) ref_doc = frappe.get_doc(dt, dn)
if dt == "Sales Invoice": if dt == "Sales Invoice":
party_type = "Customer" party_type = "Customer"
party_account = ref_doc.debit_to party_account = get_party_account_based_on_invoice_discounting(dn) or ref_doc.debit_to
else: else:
party_type = "Supplier" party_type = "Supplier"
party_account = ref_doc.credit_to party_account = ref_doc.credit_to
@ -715,6 +732,22 @@ def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_cur
"journal_entry": journal_entry "journal_entry": journal_entry
}) })
def get_party_account_based_on_invoice_discounting(sales_invoice):
party_account = None
invoice_discounting = frappe.db.sql("""
select par.accounts_receivable_discounted, par.accounts_receivable_unpaid, par.status
from `tabInvoice Discounting` par, `tabDiscounted Invoice` ch
where par.name=ch.parent
and par.docstatus=1
and ch.sales_invoice = %s
""", (sales_invoice), as_dict=1)
if invoice_discounting:
if invoice_discounting[0].status == "Disbursed":
party_account = invoice_discounting[0].accounts_receivable_discounted
elif invoice_discounting[0].status == "Settled":
party_account = invoice_discounting[0].accounts_receivable_unpaid
return party_account
def get_payment_entry(ref_doc, args): def get_payment_entry(ref_doc, args):
cost_center = ref_doc.get("cost_center") or frappe.get_cached_value('Company', ref_doc.company, "cost_center") cost_center = ref_doc.get("cost_center") or frappe.get_cached_value('Company', ref_doc.company, "cost_center")

View File

@ -18,7 +18,7 @@ def get_data():
'transactions': [ 'transactions': [
{ {
'label': _('Payment'), 'label': _('Payment'),
'items': ['Payment Entry', 'Payment Request', 'Journal Entry'] 'items': ['Payment Entry', 'Payment Request', 'Journal Entry', 'Invoice Discounting']
}, },
{ {
'label': _('Reference'), 'label': _('Reference'),

View File

@ -19,27 +19,24 @@ def verify_request():
frappe.get_request_header("X-Wc-Webhook-Signature") and \ frappe.get_request_header("X-Wc-Webhook-Signature") and \
not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()): not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()):
frappe.throw(_("Unverified Webhook Data")) frappe.throw(_("Unverified Webhook Data"))
frappe.set_user(woocommerce_settings.modified_by) frappe.set_user(woocommerce_settings.creation_user)
@frappe.whitelist(allow_guest=True) @frappe.whitelist(allow_guest=True)
def order(data=None): def order():
if not data: woocommerce_settings = frappe.get_doc("Woocommerce Settings")
verify_request() if frappe.flags.woocomm_test_order_data:
fd = frappe.flags.woocomm_test_order_data
event = "created"
if frappe.request and frappe.request.data: elif frappe.request and frappe.request.data:
verify_request()
fd = json.loads(frappe.request.data) fd = json.loads(frappe.request.data)
elif data: event = frappe.get_request_header("X-Wc-Webhook-Event")
fd = data
else: else:
return "success" return "success"
if not data:
event = frappe.get_request_header("X-Wc-Webhook-Event")
else:
event = "created"
if event == "created": if event == "created":
raw_billing_data = fd.get("billing") raw_billing_data = fd.get("billing")
customer_woo_com_email = raw_billing_data.get("email") customer_woo_com_email = raw_billing_data.get("email")
@ -73,7 +70,7 @@ def order(data=None):
new_sales_order.po_no = fd.get("id") new_sales_order.po_no = fd.get("id")
new_sales_order.woocommerce_id = fd.get("id") new_sales_order.woocommerce_id = fd.get("id")
new_sales_order.naming_series = "SO-" new_sales_order.naming_series = woocommerce_settings.sales_order_series or "SO-WOO-"
placed_order_date = created_date[0] placed_order_date = created_date[0]
raw_date = datetime.datetime.strptime(placed_order_date, "%Y-%m-%d") raw_date = datetime.datetime.strptime(placed_order_date, "%Y-%m-%d")
@ -100,10 +97,10 @@ def order(data=None):
"item_name": found_item.item_name, "item_name": found_item.item_name,
"description": found_item.item_name, "description": found_item.item_name,
"delivery_date":order_delivery_date, "delivery_date":order_delivery_date,
"uom": "Nos", "uom": woocommerce_settings.uom or _("Nos"),
"qty": item.get("quantity"), "qty": item.get("quantity"),
"rate": item.get("price"), "rate": item.get("price"),
"warehouse": "Stores" + " - " + company_abbr "warehouse": woocommerce_settings.warehouse or "Stores" + " - " + company_abbr
}) })
add_tax_details(new_sales_order,ordered_items_tax,"Ordered Item tax",0) add_tax_details(new_sales_order,ordered_items_tax,"Ordered Item tax",0)
@ -175,6 +172,7 @@ def link_customer_and_address(raw_billing_data,customer_status):
frappe.db.commit() frappe.db.commit()
def link_item(item_data,item_status): def link_item(item_data,item_status):
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
if item_status == 0: if item_status == 0:
#Create Item #Create Item
@ -189,6 +187,7 @@ def link_item(item_data,item_status):
item.item_code = "woocommerce - " + str(item_data.get("product_id")) item.item_code = "woocommerce - " + str(item_data.get("product_id"))
item.woocommerce_id = str(item_data.get("product_id")) item.woocommerce_id = str(item_data.get("product_id"))
item.item_group = "WooCommerce Products" item.item_group = "WooCommerce Products"
item.stock_uom = woocommerce_settings.uom or _("Nos")
item.save() item.save()
frappe.db.commit() frappe.db.commit()
@ -209,4 +208,4 @@ def add_tax_details(sales_order,price,desc,status):
"account_head": account_head_type, "account_head": account_head_type,
"tax_amount": price, "tax_amount": price,
"description": desc "description": desc
}) })

View File

@ -42,4 +42,15 @@ frappe.ui.form.on('Woocommerce Settings', {
frm.set_df_property("api_consumer_key", "reqd", frm.doc.enable_sync); frm.set_df_property("api_consumer_key", "reqd", frm.doc.enable_sync);
frm.set_df_property("api_consumer_secret", "reqd", frm.doc.enable_sync); frm.set_df_property("api_consumer_secret", "reqd", frm.doc.enable_sync);
} }
}); });
frappe.ui.form.on("Woocommerce Settings", "onload", function () {
frappe.call({
method: "erpnext.erpnext_integrations.doctype.woocommerce_settings.woocommerce_settings.get_series",
callback: function (r) {
$.each(r.message, function (key, value) {
set_field_options(key, value);
});
}
});
});

View File

@ -122,3 +122,9 @@ def generate_secret():
woocommerce_settings = frappe.get_doc("Woocommerce Settings") woocommerce_settings = frappe.get_doc("Woocommerce Settings")
woocommerce_settings.secret = frappe.generate_hash() woocommerce_settings.secret = frappe.generate_hash()
woocommerce_settings.save() woocommerce_settings.save()
@frappe.whitelist()
def get_series():
return {
"sales_order_series" : frappe.get_meta("Sales Order").get_options("naming_series") or "SO-WOO-",
}

View File

@ -596,3 +596,4 @@ erpnext.patches.v12_0.stock_entry_enhancements
erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019 erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019
erpnext.patches.v12_0.move_item_tax_to_item_tax_template erpnext.patches.v12_0.move_item_tax_to_item_tax_template
erpnext.patches.v11_1.set_variant_based_on erpnext.patches.v11_1.set_variant_based_on
erpnext.patches.v11_1.woocommerce_set_creation_user

View File

@ -0,0 +1,10 @@
from __future__ import unicode_literals
import frappe
def execute():
woocommerce_setting_enable_sync = frappe.db.sql("SELECT t.value FROM tabSingles t WHERE doctype = 'Woocommerce Settings' AND field = 'enable_sync'", as_dict=True)
if len(woocommerce_setting_enable_sync) and woocommerce_setting_enable_sync[0].value == '1':
frappe.db.sql("""UPDATE tabSingles
SET value = (SELECT t.value FROM tabSingles t WHERE doctype = 'Woocommerce Settings' AND field = 'modified_by')
WHERE doctype = 'Woocommerce Settings'
AND field = 'creation_user';""")

View File

@ -20,15 +20,17 @@ class TestWoocommerce(unittest.TestCase):
woo_settings.enable_sync = 1 woo_settings.enable_sync = 1
woo_settings.tax_account = "Sales Expenses - W" woo_settings.tax_account = "Sales Expenses - W"
woo_settings.f_n_f_account = "Expenses - W" woo_settings.f_n_f_account = "Expenses - W"
woo_settings.creation_user = "Administrator"
woo_settings.save(ignore_permissions=True) woo_settings.save(ignore_permissions=True)
def test_sales_order_for_woocommerece(self): def test_sales_order_for_woocommerece(self):
data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"Woocommerce","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel &times; 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]} frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"Woocommerce","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel &times; 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]}
order(data) order()
self.assertTrue(frappe.get_value("Customer",{"woocommerce_email":"tony@gmail.com"})) self.assertTrue(frappe.get_value("Customer",{"woocommerce_email":"tony@gmail.com"}))
self.assertTrue(frappe.get_value("Item",{"woocommerce_id": 56})) self.assertTrue(frappe.get_value("Item",{"woocommerce_id": 56}))
self.assertTrue(frappe.get_value("Sales Order",{"woocommerce_id":75})) self.assertTrue(frappe.get_value("Sales Order",{"woocommerce_id":75}))
frappe.flags.woocomm_test_order_data = {}
def emulate_request(): def emulate_request():
# Emulate Woocommerce Request # Emulate Woocommerce Request
@ -52,4 +54,4 @@ def emulate_request():
r = requests.post(url=url, headers=headers, data=data) r = requests.post(url=url, headers=headers, data=data)
time.sleep(5) time.sleep(5)
return r return r