Merge branch 'develop' into develop

This commit is contained in:
Deepesh Garg 2019-10-20 22:04:30 +05:30 committed by GitHub
commit dc694597f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1809 additions and 2792 deletions

View File

@ -0,0 +1,35 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Coupon Code', {
coupon_name:function(frm){
if (frm.doc.__islocal===1) {
frm.trigger("make_coupon_code");
}
},
coupon_type:function(frm){
if (frm.doc.__islocal===1) {
frm.trigger("make_coupon_code");
}
},
make_coupon_code: function(frm) {
var coupon_name=frm.doc.coupon_name;
var coupon_code;
if (frm.doc.coupon_type=='Gift Card') {
coupon_code=Math.random().toString(12).substring(2, 12).toUpperCase();
}
else if(frm.doc.coupon_type=='Promotional'){
coupon_name=coupon_name.replace(/\s/g,'');
coupon_code=coupon_name.toUpperCase().slice(0,8);
}
frm.doc.coupon_code=coupon_code;
frm.refresh_field('coupon_code');
},
refresh: function(frm) {
if (frm.doc.pricing_rule) {
frm.add_custom_button(__("Add/Edit Coupon Conditions"), function(){
frappe.set_route("Form", "Pricing Rule", frm.doc.pricing_rule);
});
}
}
});

View File

@ -0,0 +1,175 @@
{
"allow_import": 1,
"autoname": "field:coupon_name",
"creation": "2018-01-22 14:34:39.701832",
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"coupon_name",
"coupon_type",
"customer",
"column_break_4",
"coupon_code",
"pricing_rule",
"uses",
"valid_from",
"valid_upto",
"maximum_use",
"used",
"column_break_11",
"description",
"amended_from"
],
"fields": [
{
"fieldname": "coupon_name",
"fieldtype": "Data",
"label": "Coupon Name",
"reqd": 1,
"unique": 1
},
{
"fieldname": "coupon_type",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Coupon Type",
"options": "Promotional\nGift Card",
"reqd": 1
},
{
"depends_on": "eval: doc.coupon_type == \"Gift Card\"",
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"options": "Customer"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"description": "To be used to get discount",
"fieldname": "coupon_code",
"fieldtype": "Data",
"label": "Coupon Code",
"no_copy": 1,
"set_only_once": 1,
"unique": 1
},
{
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule",
"options": "Pricing Rule"
},
{
"fieldname": "uses",
"fieldtype": "Section Break",
"label": "Uses"
},
{
"fieldname": "valid_from",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Valid From"
},
{
"fieldname": "valid_upto",
"fieldtype": "Date",
"label": "Valid Upto"
},
{
"depends_on": "eval: doc.coupon_type == \"Promotional\"",
"fieldname": "maximum_use",
"fieldtype": "Int",
"label": "Maximum Use"
},
{
"default": "0",
"fieldname": "used",
"fieldtype": "Int",
"label": "Used",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "column_break_11",
"fieldtype": "Column Break"
},
{
"fieldname": "description",
"fieldtype": "Text Editor",
"label": "Coupon Description"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Coupon Code",
"print_hide": 1,
"read_only": 1
}
],
"modified": "2019-10-15 14:12:22.686986",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Coupon Code",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Website Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "coupon_name",
"track_changes": 1
}

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import (strip)
class CouponCode(Document):
def autoname(self):
self.coupon_name = strip(self.coupon_name)
self.name = self.coupon_name
if not self.coupon_code:
if self.coupon_type == "Promotional":
self.coupon_code =''.join([i for i in self.coupon_name if not i.isdigit()])[0:8].upper()
elif self.coupon_type == "Gift Card":
self.coupon_code = frappe.generate_hash()[:10].upper()
def validate(self):
if self.coupon_type == "Gift Card":
self.maximum_use = 1
if not self.customer:
frappe.throw(_("Please select the customer."))

View File

@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Coupon Code", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Coupon Code
() => frappe.tests.make('Coupon Code', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.get_item_details import get_item_details
from frappe.test_runner import make_test_objects
def test_create_test_data():
frappe.set_user("Administrator")
# create test item
if not frappe.db.exists("Item","_Test Tesla Car"):
item = frappe.get_doc({
"description": "_Test Tesla Car",
"doctype": "Item",
"has_batch_no": 0,
"has_serial_no": 0,
"inspection_required": 0,
"is_stock_item": 1,
"opening_stock":100,
"is_sub_contracted_item": 0,
"item_code": "_Test Tesla Car",
"item_group": "_Test Item Group",
"item_name": "_Test Tesla Car",
"apply_warehouse_wise_reorder_level": 0,
"warehouse":"_Test Warehouse - _TC",
"gst_hsn_code": "999800",
"valuation_rate": 5000,
"standard_rate":5000,
"item_defaults": [{
"company": "_Test Company",
"default_warehouse": "_Test Warehouse - _TC",
"default_price_list":"_Test Price List",
"expense_account": "_Test Account Cost for Goods Sold - _TC",
"buying_cost_center": "_Test Cost Center - _TC",
"selling_cost_center": "_Test Cost Center - _TC",
"income_account": "Sales - _TC"
}],
"show_in_website": 1,
"route":"-test-tesla-car",
"website_warehouse": "_Test Warehouse - _TC"
})
item.insert()
# create test item price
item_price = frappe.get_list('Item Price', filters={'item_code': '_Test Tesla Car', 'price_list': '_Test Price List'}, fields=['name'])
if len(item_price)==0:
item_price = frappe.get_doc({
"doctype": "Item Price",
"item_code": "_Test Tesla Car",
"price_list": "_Test Price List",
"price_list_rate": 5000
})
item_price.insert()
# create test item pricing rule
if not frappe.db.exists("Pricing Rule","_Test Pricing Rule for _Test Item"):
item_pricing_rule = frappe.get_doc({
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule for _Test Item",
"apply_on": "Item Code",
"items": [{
"item_code": "_Test Tesla Car"
}],
"warehouse":"_Test Warehouse - _TC",
"coupon_code_based":1,
"selling": 1,
"rate_or_discount": "Discount Percentage",
"discount_percentage": 30,
"company": "_Test Company",
"currency":"INR",
"for_price_list":"_Test Price List"
})
item_pricing_rule.insert()
# create test item sales partner
if not frappe.db.exists("Sales Partner","_Test Coupon Partner"):
sales_partner = frappe.get_doc({
"doctype": "Sales Partner",
"partner_name":"_Test Coupon Partner",
"commission_rate":2,
"referral_code": "COPART"
})
sales_partner.insert()
# create test item coupon code
if not frappe.db.exists("Coupon Code","SAVE30"):
coupon_code = frappe.get_doc({
"doctype": "Coupon Code",
"coupon_name":"SAVE30",
"coupon_code":"SAVE30",
"pricing_rule": "_Test Pricing Rule for _Test Item",
"valid_from": "2014-01-01",
"maximum_use":1,
"used":0
})
coupon_code.insert()
class TestCouponCode(unittest.TestCase):
def setUp(self):
test_create_test_data()
def tearDown(self):
frappe.set_user("Administrator")
def test_1_check_coupon_code_used_before_so(self):
coupon_code = frappe.get_doc("Coupon Code", frappe.db.get_value("Coupon Code", {"coupon_name":"SAVE30"}))
# reset used coupon code count
coupon_code.used=0
coupon_code.save()
# check no coupon code is used before sales order is made
self.assertEqual(coupon_code.get("used"),0)
def test_2_sales_order_with_coupon_code(self):
so = make_sales_order(customer="_Test Customer",selling_price_list="_Test Price List",item_code="_Test Tesla Car", rate=5000,qty=1, do_not_submit=True)
so = frappe.get_doc('Sales Order', so.name)
# check item price before coupon code is applied
self.assertEqual(so.items[0].rate, 5000)
so.coupon_code='SAVE30'
so.sales_partner='_Test Coupon Partner'
so.save()
# check item price after coupon code is applied
self.assertEqual(so.items[0].rate, 3500)
so.submit()
def test_3_check_coupon_code_used_after_so(self):
doc = frappe.get_doc("Coupon Code", frappe.db.get_value("Coupon Code", {"coupon_name":"SAVE30"}))
# check no coupon code is used before sales order is made
self.assertEqual(doc.get("used"),1)

File diff suppressed because it is too large Load Diff

View File

@ -249,6 +249,9 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
continue
if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
return item_details
if (not pricing_rule.validate_applied_rule and
pricing_rule.price_or_product_discount == "Price"):
apply_price_discount_pricing_rule(pricing_rule, item_details, args)

View File

@ -532,3 +532,31 @@ def validate_pricing_rule_for_different_cond(doc):
validate_pricing_rule_on_items(doc, d, True)
return doc
def validate_coupon_code(coupon_name):
from frappe.utils import today,getdate
coupon=frappe.get_doc("Coupon Code",coupon_name)
if coupon.valid_from:
if coupon.valid_from > getdate(today()) :
frappe.throw(_("Sorry,coupon code validity has not started"))
elif coupon.valid_upto:
if coupon.valid_upto < getdate(today()) :
frappe.throw(_("Sorry,coupon code validity has expired"))
elif coupon.used>=coupon.maximum_use:
frappe.throw(_("Sorry,coupon code are exhausted"))
else:
return
def update_coupon_code_count(coupon_name,transaction_type):
coupon=frappe.get_doc("Coupon Code",coupon_name)
if coupon:
if transaction_type=='used':
if coupon.used<coupon.maximum_use:
coupon.used=coupon.used+1
coupon.save(ignore_permissions=True)
else:
frappe.throw(_("{0} Coupon used are {1}. Allowed quantity is exhausted").format(coupon.coupon_code,coupon.used))
elif transaction_type=='cancelled':
if coupon.used>0:
coupon.used=coupon.used-1
coupon.save(ignore_permissions=True)

View File

@ -12,7 +12,7 @@ from frappe.utils import (add_days, getdate, formatdate, date_diff,
from frappe.contacts.doctype.address.address import (get_address_display,
get_default_address, get_company_address)
from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact
from erpnext.exceptions import PartyFrozen, InvalidAccountCurrency
from erpnext.exceptions import PartyFrozen, PartyDisabled, InvalidAccountCurrency
from erpnext.accounts.utils import get_fiscal_year
from erpnext import get_company_currency
@ -446,7 +446,9 @@ def validate_party_frozen_disabled(party_type, party_name):
if party_type and party_name:
if party_type in ("Customer", "Supplier"):
party = frappe.get_cached_value(party_type, party_name, ["is_frozen", "disabled"], as_dict=True)
if party.get("is_frozen"):
if party.disabled:
frappe.throw(_("{0} {1} is disabled").format(party_type, party_name), PartyDisabled)
elif party.get("is_frozen"):
frozen_accounts_modifier = frappe.db.get_single_value( 'Accounts Settings', 'frozen_accounts_modifier')
if not frozen_accounts_modifier in frappe.get_roles():
frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)

View File

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe, unittest
from erpnext.accounts.party import get_due_date
from erpnext.exceptions import PartyDisabled
from frappe.test_runner import make_test_records
test_dependencies = ['Payment Term', 'Payment Terms Template']
@ -70,7 +71,7 @@ class TestSupplier(unittest.TestCase):
po = create_purchase_order(do_not_save=True)
self.assertRaises(frappe.ValidationError, po.save)
self.assertRaises(PartyDisabled, po.save)
frappe.db.set_value("Supplier", "_Test Supplier", "disabled", 0)

View File

@ -5,3 +5,4 @@ import frappe
class PartyFrozen(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
class InvalidCurrency(frappe.ValidationError): pass
class PartyDisabled(frappe.ValidationError):pass

View File

@ -234,7 +234,7 @@ var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) {
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='temperature' \
data-pts='°C or °F' data-title='Temperature'>Temperature</a>\
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bmi' \
data-pts='bmi' data-title='BMI'>BMI</a></div>";
data-pts='' data-title='BMI'>BMI</a></div>";
me.page.main.find(".show_chart_btns").html(show_chart_btns_html);
var data = r.message;
let labels = [], datasets = [];
@ -275,7 +275,7 @@ var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) {
datasets.push({name: "Heart Rate / Pulse", values: pulse, chartType:'line'});
datasets.push({name: "Respiratory Rate", values: respiratory_rate, chartType:'line'});
}
new Chart( ".patient_vital_charts", {
new frappe.Chart( ".patient_vital_charts", {
data: {
labels: labels,
datasets: datasets
@ -283,7 +283,7 @@ var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) {
title: title,
type: 'axis-mixed', // 'axis-mixed', 'bar', 'line', 'pie', 'percentage'
height: 150,
height: 200,
colors: ['purple', '#ffa3ef', 'light-blue'],
tooltipOptions: {

View File

@ -1233,7 +1233,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
"is_return": cint(me.frm.doc.is_return),
"update_stock": in_list(['Sales Invoice', 'Purchase Invoice'], me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0,
"conversion_factor": me.frm.doc.conversion_factor,
"pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : ''
"pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '',
"coupon_code": me.frm.doc.coupon_code
};
},
@ -1742,6 +1743,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
frappe.model.set_value(me.frm.doctype + " Item", item.name, "warehouse", me.frm.doc.set_warehouse);
});
}
},
coupon_code: function() {
var me = this;
frappe.run_serially([
() => this.frm.doc.ignore_pricing_rule=1,
() => me.ignore_pricing_rule(),
() => this.frm.doc.ignore_pricing_rule=0,
() => me.apply_pricing_rule()
]);
}
});

View File

@ -5,6 +5,19 @@
frappe.provide("erpnext.shopping_cart");
var shopping_cart = erpnext.shopping_cart;
var getParams = function (url) {
var params = [];
var parser = document.createElement('a');
parser.href = url;
var query = parser.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
params[pair[0]] = decodeURIComponent(pair[1]);
}
return params;
};
frappe.ready(function() {
var full_name = frappe.session && frappe.session.user_fullname;
// update user
@ -12,7 +25,32 @@ frappe.ready(function() {
$('.navbar li[data-label="User"] a')
.html('<i class="fa fa-fixed-width fa fa-user"></i> ' + full_name);
}
// set coupon code and sales partner code
var url_args = getParams(window.location.href);
var referral_coupon_code = url_args['cc'];
var referral_sales_partner = url_args['sp'];
var d = new Date();
// expires within 30 minutes
d.setTime(d.getTime() + (0.02 * 24 * 60 * 60 * 1000));
var expires = "expires="+d.toUTCString();
if (referral_coupon_code) {
document.cookie = "referral_coupon_code=" + referral_coupon_code + ";" + expires + ";path=/";
}
if (referral_sales_partner) {
document.cookie = "referral_sales_partner=" + referral_sales_partner + ";" + expires + ";path=/";
}
referral_coupon_code=frappe.get_cookie("referral_coupon_code");
referral_sales_partner=frappe.get_cookie("referral_sales_partner");
if (referral_coupon_code && $(".tot_quotation_discount").val()==undefined ) {
$(".txtcoupon").val(referral_coupon_code);
}
if (referral_sales_partner) {
$(".txtreferral_sales_partner").val(referral_sales_partner);
}
// update login
shopping_cart.show_shoppingcart_dropdown();
shopping_cart.set_cart_count();

View File

@ -0,0 +1,8 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('DATEV Settings', {
// refresh: function(frm) {
// }
});

View File

@ -0,0 +1,105 @@
{
"autoname": "field:client",
"creation": "2019-08-13 23:56:34.259906",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"client",
"column_break_2",
"client_number",
"section_break_4",
"consultant",
"column_break_6",
"consultant_number"
],
"fields": [
{
"fieldname": "client",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Client",
"options": "Company",
"reqd": 1,
"unique": 1
},
{
"fieldname": "client_number",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Client ID",
"reqd": 1
},
{
"fieldname": "consultant",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Consultant",
"options": "Supplier"
},
{
"fieldname": "consultant_number",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Consultant ID",
"reqd": 1
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_4",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_6",
"fieldtype": "Column Break"
}
],
"modified": "2019-08-14 00:03:26.616460",
"modified_by": "Administrator",
"module": "Regional",
"name": "DATEV Settings",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

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 DATEVSettings(Document):
pass

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestDATEVSettings(unittest.TestCase):
pass

View File

@ -8,6 +8,7 @@ Provide a report and downloadable CSV according to the German DATEV format.
all required columns. Used to import the data into the DATEV Software.
"""
from __future__ import unicode_literals
import datetime
import json
from six import string_types
import frappe
@ -17,24 +18,28 @@ import pandas as pd
def execute(filters=None):
"""Entry point for frappe."""
validate_filters(filters)
validate(filters)
result = get_gl_entries(filters, as_dict=0)
columns = get_columns()
return columns, result
def validate_filters(filters):
"""Make sure all mandatory filters are present."""
def validate(filters):
"""Make sure all mandatory filters and settings are present."""
if not filters.get('company'):
frappe.throw(_('{0} is mandatory').format(_('Company')))
frappe.throw(_('<b>Company</b> is a mandatory filter.'))
if not filters.get('from_date'):
frappe.throw(_('{0} is mandatory').format(_('From Date')))
frappe.throw(_('<b>From Date</b> is a mandatory filter.'))
if not filters.get('to_date'):
frappe.throw(_('{0} is mandatory').format(_('To Date')))
frappe.throw(_('<b>To Date</b> is a mandatory filter.'))
try:
frappe.get_doc('DATEV Settings', filters.get('company'))
except frappe.DoesNotExistError:
frappe.throw(_('Please create <b>DATEV Settings</b> for Company <b>{}</b>.').format(filters.get('company')))
def get_columns():
"""Return the list of columns that will be shown in query report."""
@ -158,13 +163,84 @@ def get_gl_entries(filters, as_dict):
return gl_entries
def get_datev_csv(data):
def get_datev_csv(data, filters):
"""
Fill in missing columns and return a CSV in DATEV Format.
For automatic processing, DATEV requires the first line of the CSV file to
hold meta data such as the length of account numbers oder the category of
the data.
Arguments:
data -- array of dictionaries
filters -- dict
"""
header = [
# A = DATEV format
# DTVF = created by DATEV software,
# EXTF = created by other software
"EXTF",
# B = version of the DATEV format
# 141 = 1.41,
# 510 = 5.10,
# 720 = 7.20
"510",
# C = Data category
# 21 = Transaction batch (Buchungsstapel),
# 67 = Buchungstextkonstanten,
# 16 = Debitors/Creditors,
# 20 = Account names (Kontenbeschriftungen)
"21",
# D = Format name
# Buchungsstapel,
# Buchungstextkonstanten,
# Debitoren/Kreditoren,
# Kontenbeschriftungen
"Buchungsstapel",
# E = Format version (regarding format name)
"",
# F = Generated on
datetime.datetime.now().strftime("%Y%m%d"),
# G = Imported on -- stays empty
"",
# H = Origin (SV = other (?), RE = KARE)
"SV",
# I = Exported by
frappe.session.user,
# J = Imported by -- stays empty
"",
# K = Tax consultant number (Beraternummer)
frappe.get_value("DATEV Settings", filters.get("company"), "consultant_number") or "",
"",
# L = Tax client number (Mandantennummer)
frappe.get_value("DATEV Settings", filters.get("company"), "client_number") or "",
"",
# M = Start of the fiscal year (Wirtschaftsjahresbeginn)
frappe.utils.formatdate(frappe.defaults.get_user_default("year_start_date"), "yyyyMMdd"),
# N = Length of account numbers (Sachkontenlänge)
"4",
# O = Transaction batch start date (YYYYMMDD)
frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd"),
# P = Transaction batch end date (YYYYMMDD)
frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd"),
# Q = Description (for example, "January - February 2019 Transactions")
"{} - {} Buchungsstapel".format(
frappe.utils.formatdate(filters.get('from_date'), "MMMM yyyy"),
frappe.utils.formatdate(filters.get('to_date'), "MMMM yyyy")
),
# R = Diktatkürzel
"",
# S = Buchungstyp
# 1 = Transaction batch (Buchungsstapel),
# 2 = Annual financial statement (Jahresabschluss)
"1",
# T = Rechnungslegungszweck
"",
# U = Festschreibung
"",
# V = Kontoführungs-Währungskennzeichen des Geldkontos
frappe.get_value("Company", filters.get("company"), "default_currency")
]
columns = [
# All possible columns must tbe listed here, because DATEV requires them to
# be present in the CSV.
@ -324,9 +400,10 @@ def get_datev_csv(data):
data_df = pd.DataFrame.from_records(data)
result = empty_df.append(data_df)
result["Belegdatum"] = pd.to_datetime(result["Belegdatum"])
result['Belegdatum'] = pd.to_datetime(result['Belegdatum'])
return result.to_csv(
header = ';'.join(header).encode('latin_1')
data = result.to_csv(
sep=b';',
# European decimal seperator
decimal=',',
@ -342,6 +419,7 @@ def get_datev_csv(data):
columns=columns
)
return header + b'\r\n' + data
@frappe.whitelist()
def download_datev_csv(filters=None):
@ -359,15 +437,9 @@ def download_datev_csv(filters=None):
if isinstance(filters, string_types):
filters = json.loads(filters)
validate_filters(filters)
validate(filters)
data = get_gl_entries(filters, as_dict=1)
filename = 'DATEV_Buchungsstapel_{}-{}_bis_{}'.format(
filters.get('company'),
filters.get('from_date'),
filters.get('to_date')
)
frappe.response['result'] = get_datev_csv(data)
frappe.response['doctype'] = filename
frappe.response['result'] = get_datev_csv(data, filters)
frappe.response['doctype'] = 'EXTF_Buchungsstapel'
frappe.response['type'] = 'csv'

View File

@ -8,7 +8,7 @@ import unittest
from erpnext.accounts.party import get_due_date
from frappe.test_runner import make_test_records
from erpnext.exceptions import PartyFrozen
from erpnext.exceptions import PartyFrozen, PartyDisabled
from frappe.utils import flt
from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding
from erpnext.tests.utils import create_test_contact_and_address
@ -178,7 +178,7 @@ class TestCustomer(unittest.TestCase):
so = make_sales_order(do_not_save=True)
self.assertRaises(frappe.ValidationError, so.save)
self.assertRaises(PartyDisabled, so.save)
frappe.db.set_value("Customer", "_Test Customer", "disabled", 0)

View File

@ -1904,7 +1904,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Additional Discount",
"label": "Additional Discount and Coupon Code",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -1920,6 +1920,74 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "coupon_code",
"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": "Coupon Code",
"length": 0,
"no_copy": 0,
"options": "Coupon Code",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "referral_sales_partner",
"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": "Referral Sales Partner",
"length": 0,
"no_copy": 0,
"options": "Sales Partner",
"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,
@ -3263,7 +3331,7 @@
"istable": 0,
"max_attachments": 1,
"menu_index": 0,
"modified": "2019-06-26 01:00:21.545591",
"modified": "2019-10-14 01:00:21.545591",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",

View File

@ -142,6 +142,9 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
if customer:
target.customer = customer.name
target.customer_name = customer.customer_name
if source.referral_sales_partner:
target.sales_partner=source.referral_sales_partner
target.commission_rate=frappe.get_value('Sales Partner', source.referral_sales_partner, 'commission_rate')
target.ignore_pricing_rule = 1
target.flags.ignore_permissions = ignore_permissions
target.run_method("set_missing_values")

View File

@ -79,6 +79,7 @@
"loyalty_points",
"loyalty_amount",
"section_break_48",
"coupon_code",
"apply_discount_on",
"base_discount_amount",
"column_break_50",
@ -678,7 +679,13 @@
"collapsible_depends_on": "discount_amount",
"fieldname": "section_break_48",
"fieldtype": "Section Break",
"label": "Additional Discount"
"label": "Additional Discount and Coupon Code"
},
{
"fieldname": "coupon_code",
"fieldtype": "Link",
"label": "Coupon Code",
"options": "Coupon Code"
},
{
"default": "Grand Total",
@ -1185,7 +1192,7 @@
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"modified": "2019-10-10 08:46:07.540565",
"modified": "2019-10-14 08:46:07.540565",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",

View File

@ -46,6 +46,10 @@ class SalesOrder(SellingController):
self.validate_serial_no_based_delivery()
validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_order_reference)
if self.coupon_code:
from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
validate_coupon_code(self.coupon_code)
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self)
@ -177,6 +181,9 @@ class SalesOrder(SellingController):
self.update_blanket_order()
update_linked_doc(self.doctype, self.name, self.inter_company_order_reference)
if self.coupon_code:
from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
update_coupon_code_count(self.coupon_code,'used')
def on_cancel(self):
super(SalesOrder, self).on_cancel()
@ -195,6 +202,9 @@ class SalesOrder(SellingController):
self.update_blanket_order()
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_order_reference)
if self.coupon_code:
from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
update_coupon_code_count(self.coupon_code,'cancelled')
def update_project(self):
if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') != "Each Transaction":

View File

@ -24,5 +24,11 @@ frappe.ui.form.on('Sales Partner', {
}
}
};
},
referral_code:function(frm){
if (frm.doc.referral_code) {
frm.doc.referral_code=frm.doc.referral_code.toUpperCase();
frm.refresh_field('referral_code');
}
}
});

View File

@ -510,6 +510,73 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_16",
"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,
"depends_on": "",
"description": "To Track inbound purchase",
"fetch_if_empty": 0,
"fieldname": "referral_code",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Referral Code",
"length": 8,
"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": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -779,7 +846,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-21 16:26:45.447265",
"modified": "2019-10-14 16:26:45.447265",
"modified_by": "Administrator",
"module": "Setup",
"name": "Sales Partner",

View File

@ -537,3 +537,29 @@ def get_address_territory(address_name):
def show_terms(doc):
return doc.tc_name
@frappe.whitelist(allow_guest=True)
def apply_coupon_code(applied_code,applied_referral_sales_partner):
quotation = True
if applied_code:
coupon_list=frappe.get_all('Coupon Code', filters={"docstatus": ("<", "2"), 'coupon_code':applied_code }, fields=['name'])
if coupon_list:
coupon_name=coupon_list[0].name
from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
validate_coupon_code(coupon_name)
quotation = _get_cart_quotation()
quotation.coupon_code=coupon_name
quotation.flags.ignore_permissions = True
quotation.save()
if applied_referral_sales_partner:
sales_partner_list=frappe.get_all('Sales Partner', filters={'docstatus': 0, 'referral_code':applied_referral_sales_partner }, fields=['name'])
if sales_partner_list:
sales_partner_name=sales_partner_list[0].name
quotation.referral_sales_partner=sales_partner_name
quotation.flags.ignore_permissions = True
quotation.save()
else:
frappe.throw(_("Please enter valid coupon code !!"))
else:
frappe.throw(_("Please enter coupon code !!"))
return quotation

View File

@ -275,6 +275,40 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "show_apply_coupon_code_in_website",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Show Apply Coupon Code",
"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,
@ -679,7 +713,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2019-01-26 13:54:24.575322",
"modified": "2019-10-14 13:54:24.575322",
"modified_by": "Administrator",
"module": "Shopping Cart",
"name": "Shopping Cart Settings",

View File

@ -20,6 +20,7 @@ $.extend(shopping_cart, {
shopping_cart.bind_change_qty();
shopping_cart.bind_change_notes();
shopping_cart.bind_dropdown_cart_buttons();
shopping_cart.bind_coupon_code();
},
bind_address_select: function() {
@ -193,6 +194,29 @@ $.extend(shopping_cart, {
}
}
});
},
bind_coupon_code: function() {
$(".bt-coupon").on("click", function() {
shopping_cart.apply_coupon_code(this);
});
},
apply_coupon_code: function(btn) {
return frappe.call({
type: "POST",
method: "erpnext.shopping_cart.cart.apply_coupon_code",
btn: btn,
args : {
applied_code : $('.txtcoupon').val(),
applied_referral_sales_partner: $('.txtreferral_sales_partner').val()
},
callback: function(r) {
if (r && r.message){
location.reload();
}
}
});
}
});

View File

@ -4,6 +4,16 @@
{% set select_address = True %}
{% endif %}
{% set show_coupon_code = frappe.db.get_single_value('Shopping Cart Settings', 'show_apply_coupon_code_in_website') %}
{% if show_coupon_code == 1%}
<div class="mb-3">
<div class="row no-gutters">
<input type="text" class="txtcoupon form-control mr-3 w-25" placeholder="Enter Coupon Code" name="txtcouponcode" ></input>
<button class="btn btn-primary btn-sm bt-coupon">{{ _("Apply Coupon Code") }}</button>
<input type="hidden" class="txtreferral_sales_partner" placeholder="Enter Sales Partner" name="txtreferral_sales_partner" type="text"></input>
</div>
</div>
{% endif %}
<div class="mb-3" data-section="shipping-address">
<h6 class="text-uppercase">{{ _("Shipping Address") }}</h6>
<div class="row no-gutters" data-fieldname="shipping_address_name">

View File

@ -22,6 +22,71 @@
{% endif %}
{% endfor %}
{% if doc.doctype == 'Quotation' %}
{% if doc.coupon_code %}
<tr>
<th class="text-right" colspan="2">
{{ _("Discount") }}
</th>
<th class="text-right tot_quotation_discount">
{% set tot_quotation_discount = [] %}
{%- for item in doc.items -%}
{% if tot_quotation_discount.append((((item.price_list_rate * item.qty)
* item.discount_percentage) / 100)) %}{% endif %}
{% endfor %}
{{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }}
</th>
</tr>
{% endif %}
{% endif %}
{% if doc.doctype == 'Sales Order' %}
{% if doc.coupon_code %}
<tr>
<th class="text-right" colspan="2">
{{ _("Total Amount") }}
</th>
<th class="text-right">
<span>
{% set total_amount = [] %}
{%- for item in doc.items -%}
{% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %}
{% endfor %}
{{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }}
</span>
</th>
</tr>
<tr>
<th class="text-right" colspan="2">
{{ _("Applied Coupon Code") }}
</th>
<th class="text-right">
<span>
{%- for row in frappe.get_all(doctype="Coupon Code",
fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%}
<span>{{ row.coupon_code }}</span>
{% endfor %}
</span>
</th>
</tr>
<tr>
<th class="text-right" colspan="2">
{{ _("Discount") }}
</th>
<th class="text-right">
<span>
{% set tot_SO_discount = [] %}
{%- for item in doc.items -%}
{% if tot_SO_discount.append((((item.price_list_rate * item.qty)
* item.discount_percentage) / 100)) %}{% endif %}
{% endfor %}
{{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }}
</span>
</th>
</tr>
{% endif %}
{% endif %}
<tr>
<th class="text-right" colspan="2">
{{ _("Grand Total") }}

View File

@ -7,6 +7,7 @@
"allow_multiple": 1,
"allow_print": 0,
"amount": 0.0,
"amount_based_on_field": 0,
"creation": "2016-06-24 15:50:33.196990",
"doc_type": "Address",
"docstatus": 0,
@ -15,19 +16,23 @@
"is_standard": 1,
"login_required": 1,
"max_attachment_size": 0,
"modified": "2016-12-07 04:17:02.020768",
"modified": "2019-10-15 06:55:30.405119",
"modified_by": "Administrator",
"module": "Utilities",
"name": "addresses",
"owner": "Administrator",
"published": 1,
"route": "address",
"route_to_success_link": 0,
"show_attachments": 0,
"show_in_grid": 0,
"show_sidebar": 0,
"sidebar_items": [],
"success_url": "/addresses",
"title": "Address",
"web_form_fields": [
{
"allow_read_on_all_link_options": 0,
"description": "",
"fieldname": "address_title",
"fieldtype": "Data",
@ -36,9 +41,11 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "address_type",
"fieldtype": "Select",
"hidden": 0,
@ -47,9 +54,11 @@
"max_value": 0,
"options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther",
"read_only": 0,
"reqd": 1
"reqd": 1,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "address_line1",
"fieldtype": "Data",
"hidden": 0,
@ -57,9 +66,11 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 1
"reqd": 1,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "address_line2",
"fieldtype": "Data",
"hidden": 0,
@ -67,9 +78,11 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "city",
"fieldtype": "Data",
"hidden": 0,
@ -77,9 +90,11 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 1
"reqd": 1,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "state",
"fieldtype": "Data",
"hidden": 0,
@ -87,9 +102,11 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "pincode",
"fieldtype": "Data",
"hidden": 0,
@ -97,9 +114,11 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 1,
"fieldname": "country",
"fieldtype": "Link",
"hidden": 0,
@ -108,17 +127,21 @@
"max_value": 0,
"options": "Country",
"read_only": 0,
"reqd": 1
"reqd": 1,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldtype": "Column Break",
"hidden": 0,
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "email_id",
"fieldtype": "Data",
"hidden": 0,
@ -126,9 +149,11 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"fieldname": "phone",
"fieldtype": "Data",
"hidden": 0,
@ -136,9 +161,11 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 1
"reqd": 1,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"default": "0",
"description": "",
"fieldname": "is_primary_address",
@ -148,9 +175,11 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0
"reqd": 0,
"show_in_filter": 0
},
{
"allow_read_on_all_link_options": 0,
"default": "0",
"description": "",
"fieldname": "is_shipping_address",
@ -160,7 +189,8 @@
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0
"reqd": 0,
"show_in_filter": 0
}
]
}