From 58b2a101aee16bddef75f704042e81dedc0abd71 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 7 Mar 2013 11:42:14 +0530 Subject: [PATCH 01/10] aii: testcases for sales invoice gl entries --- .../doctype/sales_invoice/sales_invoice.py | 2 +- .../sales_invoice/test_sales_invoice.py | 237 ++++++++++++++---- .../delivery_note/test_delivery_note.py | 2 +- 3 files changed, 184 insertions(+), 57 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 47e3195d71..b31d54919c 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -8,7 +8,7 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index fb290d27e3..1f165f0c33 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -38,72 +38,199 @@ class TestSalesInvoice(unittest.TestCase): si.insert() si.submit() - self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", "status"), - "Billed") + self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", + "status"), "Billed") self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Billed") si.cancel() - self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", "status"), - "Submitted") + self.assertEquals(webnotes.conn.get_value("Time Log Batch", "_T-Time Log Batch-00001", + "status"), "Submitted") self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Batched for Billing") + def test_sales_invoice_gl_entry_without_aii(self): + si = webnotes.bean(webnotes.copy_doclist(test_records[1])) + si.insert() + si.submit() + webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + + gl_entries = webnotes.conn.sql("""select account, debit, credit + from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s + order by account asc""", si.doc.name, as_dict=1) + self.assertTrue(gl_entries) + expected_values = sorted([ + [si.doc.debit_to, 630.0, 0.0], + [test_records[1][1]["income_account"], 0.0, 500.0], + [test_records[1][2]["account_head"], 0.0, 80.0], + [test_records[1][3]["account_head"], 0.0, 50.0], + ]) + + for i, gle in enumerate(gl_entries): + self.assertEquals(expected_values[i][0], gle.account) + self.assertEquals(expected_values[i][1], gle.debit) + self.assertEquals(expected_values[i][2], gle.credit) + + def test_sales_invoice_gl_entry_with_aii(self): + webnotes.defaults.set_global_default("auto_inventory_accounting", 1) + + self._insert_purchase_receipt() + dn = self._insert_delivery_note() + + si_against_dn = webnotes.copy_doclist(test_records[1]) + si_against_dn[1]["delivery_note"] = dn.doc.name + si = webnotes.bean(si_against_dn) + si.insert() + si.submit() + + gl_entries = webnotes.conn.sql("""select account, debit, credit + from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s + order by account asc""", si.doc.name, as_dict=1) + self.assertTrue(gl_entries) + + expected_values = sorted([ + [si.doc.debit_to, 630.0, 0.0], + [test_records[1][1]["income_account"], 0.0, 500.0], + [test_records[1][2]["account_head"], 0.0, 80.0], + [test_records[1][3]["account_head"], 0.0, 50.0], + ["Stock Delivered But Not Billed - _TC", 0.0, 375.0], + [test_records[1][1]["expense_account"], 375.0, 0.0] + ]) + print expected_values + print gl_entries + for i, gle in enumerate(gl_entries): + self.assertEquals(expected_values[i][0], gle.account) + self.assertEquals(expected_values[i][1], gle.debit) + self.assertEquals(expected_values[i][2], gle.credit) + + webnotes.defaults.set_global_default("auto_inventory_accounting", 1) + + + def _insert_purchase_receipt(self): + from stock.doctype.purchase_receipt.test_purchase_receipt import test_records \ + as pr_test_records + pr = webnotes.bean(copy=pr_test_records[0]) + pr.run_method("calculate_taxes_and_totals") + pr.insert() + pr.submit() + + def _insert_delivery_note(self): + from stock.doctype.delivery_note.test_delivery_note import test_records \ + as dn_test_records + dn = webnotes.bean(copy=dn_test_records[0]) + dn.insert() + dn.submit() + return dn test_dependencies = ["Journal Voucher"] -test_records = [[ - { - "naming_series": "_T-Sales Invoice-", - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "debit_to": "_Test Customer - _TC", - "customer": "_Test Customer", - "customer_name": "_Test Customer", - "doctype": "Sales Invoice", - "due_date": "2013-01-23", - "fiscal_year": "_Test Fiscal Year 2013", - "grand_total": 561.8, - "grand_total_export": 561.8, - "net_total": 500.0, - "plc_conversion_rate": 1.0, - "posting_date": "2013-01-23", - "price_list_currency": "INR", - "price_list_name": "_Test Price List", - "territory": "_Test Territory" - }, - { - "amount": 500.0, - "basic_rate": 500.0, - "description": "138-CMS Shoe", - "doctype": "Sales Invoice Item", - "export_amount": 500.0, - "export_rate": 500.0, - "income_account": "Sales - _TC", - "cost_center": "_Test Cost Center - _TC", - "item_name": "138-CMS Shoe", - "parentfield": "entries", - "qty": 1.0 - }, - { - "account_head": "_Test Account VAT - _TC", - "charge_type": "On Net Total", - "description": "VAT", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", - "tax_amount": 30.0, - }, - { - "account_head": "_Test Account Service Tax - _TC", - "charge_type": "On Net Total", - "description": "Service Tax", - "doctype": "Sales Taxes and Charges", - "parentfield": "other_charges", - "tax_amount": 31.8, - } -]] \ No newline at end of file +test_records = [ + [ + { + "naming_series": "_T-Sales Invoice-", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "debit_to": "_Test Customer - _TC", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "doctype": "Sales Invoice", + "due_date": "2013-01-23", + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total": 561.8, + "grand_total_export": 561.8, + "net_total": 500.0, + "plc_conversion_rate": 1.0, + "posting_date": "2013-01-23", + "price_list_currency": "INR", + "price_list_name": "_Test Price List", + "territory": "_Test Territory" + }, + { + "amount": 500.0, + "basic_rate": 500.0, + "description": "138-CMS Shoe", + "doctype": "Sales Invoice Item", + "export_amount": 500.0, + "export_rate": 500.0, + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + "item_name": "138-CMS Shoe", + "parentfield": "entries", + "qty": 1.0 + }, + { + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "tax_amount": 30.0, + }, + { + "account_head": "_Test Account Service Tax - _TC", + "charge_type": "On Net Total", + "description": "Service Tax", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "tax_amount": 31.8, + } + ], + [ + { + "naming_series": "_T-Sales Invoice-", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "debit_to": "_Test Customer - _TC", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "doctype": "Sales Invoice", + "due_date": "2013-01-23", + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total": 630.0, + "grand_total_export": 630.0, + "net_total": 500.0, + "plc_conversion_rate": 1.0, + "posting_date": "2013-03-07", + "price_list_currency": "INR", + "price_list_name": "_Test Price List", + "territory": "_Test Territory" + }, + { + "item_code": "_Test Item", + "item_name": "_Test Item", + "description": "_Test Item", + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "qty": 1.0, + "basic_rate": 500.0, + "amount": 500.0, + "export_rate": 500.0, + "export_amount": 500.0, + "income_account": "Sales - _TC", + "expense_account": "_Test Account Cost for Goods Sold", + "cost_center": "_Test Cost Center - _TC", + }, + { + "account_head": "_Test Account VAT - _TC", + "charge_type": "On Net Total", + "description": "VAT", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "tax_amount": 80.0, + }, + { + "account_head": "_Test Account Service Tax - _TC", + "charge_type": "On Net Total", + "description": "Service Tax", + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "tax_amount": 50.0, + } + ], +] \ No newline at end of file diff --git a/stock/doctype/delivery_note/test_delivery_note.py b/stock/doctype/delivery_note/test_delivery_note.py index c2bb5d07ca..acdf8b9e07 100644 --- a/stock/doctype/delivery_note/test_delivery_note.py +++ b/stock/doctype/delivery_note/test_delivery_note.py @@ -109,7 +109,7 @@ test_records = [ "description": "CPU", "doctype": "Delivery Note Item", "item_code": "_Test Item", - "item_name": "CPU", + "item_name": "_Test Item", "parentfield": "delivery_note_details", "qty": 5.0, "basic_rate": 100.0, From 9f3c72775365d47d0d15f28835fb506b35e8ec43 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 7 Mar 2013 14:47:41 +0530 Subject: [PATCH 02/10] website navbar color change --- website/css/website.css | 8 +- .../style_settings/custom_template.css | 75 +++++++++++++++++++ .../doctype/style_settings/style_settings.js | 1 + .../doctype/style_settings/style_settings.txt | 37 +++++++-- 4 files changed, 108 insertions(+), 13 deletions(-) diff --git a/website/css/website.css b/website/css/website.css index 956f22e8bc..a0f4e1b3ca 100644 --- a/website/css/website.css +++ b/website/css/website.css @@ -4,14 +4,14 @@ div.outer { box-shadow: 0px 0px 3px rgba(0,0,0,0.9); background-color: #fff; border-radius: 5px; - padding: 20px; - margin: 30px -20px 10px -20px; + padding: 30px; + margin: 30px -30px 10px -30px; min-height: 400px; overflow: hidden; } .outer .navbar { - margin: -20px -20px 10px -20px; + margin: -30px -30px 10px -30px; } footer { @@ -121,5 +121,3 @@ p, li { .avatar-x-large img { width: 100px; } - -/* */ \ No newline at end of file diff --git a/website/doctype/style_settings/custom_template.css b/website/doctype/style_settings/custom_template.css index 9009bbe18a..52897447bd 100644 --- a/website/doctype/style_settings/custom_template.css +++ b/website/doctype/style_settings/custom_template.css @@ -26,3 +26,78 @@ h1, h2, h3, h4, h5 { font-family: '{{ doc.heading_font}}', Arial, 'Helvetica Neue' !important; } {% endif %} + +/* Bootstrap Navbar */ + +.navbar-inverse .navbar-inner { + background-color: #{{ doc.top_bar_background or "444"}}; + background-repeat: repeat-x; + border-color: transparent; + background-image: none; +} + +.navbar-inverse .brand, +.navbar-inverse .nav > li > a { + color: #{{ doc.top_bar_foreground or "fff"}}; + text-shadow: none; +} + +.navbar-inverse .brand:hover, +.navbar-inverse .nav > li > a:hover, +.navbar-inverse .brand:focus, +.navbar-inverse .nav > li > a:focus { + color: #{{ doc.top_bar_foreground or "fff"}}; +} + +.navbar-inverse .brand { + color: #{{ doc.top_bar_foreground or "fff"}}; +} + +.navbar-inverse .navbar-text { + color: #999999; +} + +.navbar-inverse .nav > li > a:focus, +.navbar-inverse .nav > li > a:hover { + color: #{{ doc.top_bar_foreground or "fff"}}; + background-color: transparent; +} + +.navbar-inverse .nav .active > a, +.navbar-inverse .nav .active > a:hover, +.navbar-inverse .nav .active > a:focus { + color: #{{ doc.top_bar_foreground or "fff"}}; + background-color: transparent; +} + +.navbar-inverse .navbar-link { + color: #999999; +} + +.navbar-inverse .navbar-link:hover, +.navbar-inverse .navbar-link:focus { + color: #{{ doc.top_bar_foreground or "fff"}}; +} + +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + +} +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: #424242; + text-decoration: none; + background-color: transparent; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #{{ doc.top_bar_foreground or "fff"}}; + border-bottom-color: #{{ doc.top_bar_foreground or "fff"}}; +} diff --git a/website/doctype/style_settings/style_settings.js b/website/doctype/style_settings/style_settings.js index 54091a38f2..6a17db99ee 100644 --- a/website/doctype/style_settings/style_settings.js +++ b/website/doctype/style_settings/style_settings.js @@ -18,5 +18,6 @@ cur_frm.cscript.onload_post_render = function() { wn.require('lib/public/js/lib/jscolor/jscolor.js'); cur_frm.fields_dict.background_color.input.className = 'color'; + cur_frm.fields_dict.top_bar_background.input.className = 'color'; jscolor.bind(); } \ No newline at end of file diff --git a/website/doctype/style_settings/style_settings.txt b/website/doctype/style_settings/style_settings.txt index 5f53441ea8..0063ba975e 100644 --- a/website/doctype/style_settings/style_settings.txt +++ b/website/doctype/style_settings/style_settings.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-01-10 16:34:32", + "creation": "2013-01-25 11:35:10", "docstatus": 0, - "modified": "2013-01-22 14:57:25", + "modified": "2013-03-07 14:46:51", "modified_by": "Administrator", "owner": "Administrator" }, @@ -24,18 +24,15 @@ "permlevel": 0 }, { - "create": 1, "doctype": "DocPerm", "name": "__common__", "parent": "Style Settings", "parentfield": "permissions", "parenttype": "DocType", - "permlevel": 0, "read": 1, - "report": 1, + "report": 0, "role": "Website Manager", - "submit": 0, - "write": 1 + "submit": 0 }, { "doctype": "DocType", @@ -56,6 +53,20 @@ "fieldtype": "Data", "label": "Background Color" }, + { + "doctype": "DocField", + "fieldname": "top_bar_background", + "fieldtype": "Data", + "label": "Top Bar Background" + }, + { + "description": "000 is black, fff is white", + "doctype": "DocField", + "fieldname": "top_bar_foreground", + "fieldtype": "Select", + "label": "Top Bar Foreground", + "options": "000\nFFF" + }, { "doctype": "DocField", "fieldname": "cb0", @@ -115,6 +126,16 @@ "print_hide": 1 }, { - "doctype": "DocPerm" + "create": 1, + "doctype": "DocPerm", + "permlevel": 0, + "write": 1 + }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "permlevel": 1 } ] \ No newline at end of file From f1e274c8b47d34bdcb70631491a2a5830faa485d Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 7 Mar 2013 16:34:59 +0530 Subject: [PATCH 03/10] when cancelling stock ledger entry, change modified and modified by --- stock/doctype/stock_ledger/stock_ledger.py | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/stock/doctype/stock_ledger/stock_ledger.py b/stock/doctype/stock_ledger/stock_ledger.py index 05fc0e0642..5dff992ae4 100644 --- a/stock/doctype/stock_ledger/stock_ledger.py +++ b/stock/doctype/stock_ledger/stock_ledger.py @@ -17,7 +17,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import add_days, cstr, flt, nowdate, cint +from webnotes.utils import add_days, cstr, flt, nowdate, cint, now from webnotes.model.doc import Document from webnotes.model.bean import getlist from webnotes.model.code import get_obj @@ -49,7 +49,7 @@ class DocType: serial_nos = get_valid_serial_nos(d.serial_no) for s in serial_nos: s = s.strip() - sr_war = sql("select warehouse,name from `tabSerial No` where name = '%s'" % (s)) + sr_war = webnotes.conn.sql("select warehouse,name from `tabSerial No` where name = '%s'" % (s)) if not sr_war: msgprint("Serial No %s does not exists"%s, raise_exception = 1) elif not sr_war[0][0]: @@ -81,7 +81,7 @@ class DocType: def set_pur_serial_no_values(self, obj, serial_no, d, s, new_rec, rejected=None): - item_details = sql("""select item_group, warranty_period + item_details = webnotes.conn.sql("""select item_group, warranty_period from `tabItem` where name = '%s' and (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00' or end_of_life > now()) """ %(d.item_code), as_dict=1) @@ -112,7 +112,7 @@ class DocType: def update_serial_purchase_details(self, obj, d, serial_no, is_submit, purpose = '', rejected=None): - exists = sql("select name, status, docstatus from `tabSerial No` where name = '%s'" % (serial_no)) + exists = webnotes.conn.sql("select name, status, docstatus from `tabSerial No` where name = '%s'" % (serial_no)) if is_submit: if exists and exists[0][2] != 2 and purpose not in ['Material Transfer', 'Sales Return']: msgprint("Serial No: %s already %s" % (serial_no, exists and exists[0][1]), raise_exception = 1) @@ -126,15 +126,15 @@ class DocType: if exists and exists[0][1] == 'Delivered' and exists[0][2] != 2: msgprint("Serial No: %s is already delivered, you can not cancel the document." % serial_no, raise_exception=1) elif purpose == 'Material Transfer': - sql("update `tabSerial No` set status = 'In Store', purchase_document_type = '', purchase_document_no = '', warehouse = '%s' where name = '%s'" % (d.s_warehouse, serial_no)) + webnotes.conn.sql("update `tabSerial No` set status = 'In Store', purchase_document_type = '', purchase_document_no = '', warehouse = '%s' where name = '%s'" % (d.s_warehouse, serial_no)) elif purpose == 'Sales Return': - sql("update `tabSerial No` set status = 'Delivered', purchase_document_type = '', purchase_document_no = '' where name = '%s'" % serial_no) + webnotes.conn.sql("update `tabSerial No` set status = 'Delivered', purchase_document_type = '', purchase_document_no = '' where name = '%s'" % serial_no) else: - sql("update `tabSerial No` set docstatus = 2, status = 'Not in Use', purchase_document_type = '', purchase_document_no = '', purchase_date = null, purchase_rate = 0, supplier = null, supplier_name = '', supplier_address = '', warehouse = '' where name = '%s'" % serial_no) + webnotes.conn.sql("update `tabSerial No` set docstatus = 2, status = 'Not in Use', purchase_document_type = '', purchase_document_no = '', purchase_date = null, purchase_rate = 0, supplier = null, supplier_name = '', supplier_address = '', warehouse = '' where name = '%s'" % serial_no) def check_serial_no_exists(self, serial_no, item_code): - chk = sql("select name, status, docstatus, item_code from `tabSerial No` where name = %s", (serial_no), as_dict=1) + chk = webnotes.conn.sql("select name, status, docstatus, item_code from `tabSerial No` where name = %s", (serial_no), as_dict=1) if not chk: msgprint("Serial No: %s does not exists in the system" % serial_no, raise_exception=1) elif chk and chk[0]['item_code'] != item_code: @@ -169,7 +169,7 @@ class DocType: self.check_serial_no_exists(serial_no, d.item_code) self.set_delivery_serial_no_values(obj, serial_no) else: - sql("update `tabSerial No` set docstatus = 0, status = 'In Store', delivery_document_type = '', delivery_document_no = '', delivery_date = null, customer = null, customer_name = '', delivery_address = '', territory = null where name = '%s'" % (serial_no)) + webnotes.conn.sql("update `tabSerial No` set docstatus = 0, status = 'In Store', delivery_document_type = '', delivery_document_no = '', delivery_date = null, customer = null, customer_name = '', delivery_address = '', territory = null where name = '%s'" % (serial_no)) def update_serial_record(self, obj, fname, is_submit = 1, is_incoming = 0): @@ -202,8 +202,10 @@ class DocType: if v.get('is_cancelled') == 'Yes': v['actual_qty'] = -flt(v['actual_qty']) # cancel matching entry - sql("update `tabStock Ledger Entry` set is_cancelled='Yes' where voucher_no=%s \ - and voucher_type=%s", (v['voucher_no'], v['voucher_type'])) + webnotes.conn.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes', + modified=%s, modified_by=%s + where voucher_no=%s and voucher_type=%s""", + (now(), webnotes.session.user, v['voucher_no'], v['voucher_type'])) if v.get("actual_qty"): sle_id = self.make_entry(v) @@ -230,5 +232,5 @@ class DocType: """ Repost everything! """ - for wh in sql("select name from tabWarehouse"): + for wh in webnotes.conn.sql("select name from tabWarehouse"): get_obj('Warehouse', wh[0]).repost_stock() From 4f129239030db439a7eb04b23d6339ad9c25701d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 7 Mar 2013 18:49:50 +0530 Subject: [PATCH 04/10] fixes in sales team percentage validation --- selling/doctype/sales_common/sales_common.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py index 47d139f955..12ae4a8c90 100644 --- a/selling/doctype/sales_common/sales_common.py +++ b/selling/doctype/sales_common/sales_common.py @@ -341,12 +341,10 @@ class DocType(TransactionBase): # ======================================================================== # it indicates % contribution of sales person in sales def get_allocated_sum(self,obj): - sum = 0 - for d in getlist(obj.doclist,'sales_team'): - sum += flt(d.allocated_percentage) - if (flt(sum) != 100) and getlist(obj.doclist,'sales_team'): - msgprint("Total Allocated % of Sales Persons should be 100%") - raise Exception + sales_team_list = obj.doclist.get({"parentfield": "sales_team"}) + total_allocation = sum([flt(d.allocated_percentage) for d in sales_team_list]) + if sales_team_list and total_allocation != 100.0: + msgprint("Total Allocated %% of Sales Persons should be 100%", raise_exception=True) # Check Conversion Rate (i.e. it will not allow conversion rate to be 1 for Currency other than default currency set in Global Defaults) # =========================================================================== From 676a568f5a13fd469699412c8b0b5381352a7380 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 7 Mar 2013 18:51:10 +0530 Subject: [PATCH 05/10] added blogger, updated style and blogs --- website/css/website.css | 19 +++- website/doctype/blog/blog.py | 7 ++ website/doctype/blog/blog.txt | 27 +++++- website/doctype/blogger/__init__.py | 0 website/doctype/blogger/blogger.py | 18 ++++ website/doctype/blogger/blogger.txt | 95 +++++++++++++++++++ .../style_settings/custom_template.css | 32 ++++++- .../doctype/style_settings/style_settings.js | 1 + .../doctype/style_settings/style_settings.py | 30 +++++- .../doctype/style_settings/style_settings.txt | 65 ++++++++++--- website/helpers/blog.py | 46 ++++----- website/page/website_home/website_home.js | 5 + website/templates/html/blog_page.html | 41 +++++--- website/templates/html/outer.html | 2 +- website/templates/js/blog.js | 28 ++++-- website/templates/pages/blog.html | 3 +- 16 files changed, 341 insertions(+), 78 deletions(-) create mode 100644 website/doctype/blogger/__init__.py create mode 100644 website/doctype/blogger/blogger.py create mode 100644 website/doctype/blogger/blogger.txt diff --git a/website/css/website.css b/website/css/website.css index a0f4e1b3ca..dc5b9670b3 100644 --- a/website/css/website.css +++ b/website/css/website.css @@ -1,9 +1,4 @@ div.outer { - -moz-box-shadow: 0px 0px 3px rgba(0,0,0,0.9); - -webkit-box-shadow: 0px 0px 3px rgba(0,0,0,0.9); - box-shadow: 0px 0px 3px rgba(0,0,0,0.9); - background-color: #fff; - border-radius: 5px; padding: 30px; margin: 30px -30px 10px -30px; min-height: 400px; @@ -24,6 +19,8 @@ footer { border: 0px; border-bottom: 1px solid #ddd; border-radius: 0px; + padding-right: 30px; + padding-left: 30px; } p, li { @@ -98,6 +95,18 @@ p, li { width: 30px; } +.avatar-medium { + margin-right: 5px; + width: 48px; + height: 48px; + border-radius: 48px; + -moz-border-radius: 48px; + -webkit-border-radius: 48px; +} +.avatar-medium img { + width: 48px; +} + .avatar-large { margin-right: 10px; width: 72px; diff --git a/website/doctype/blog/blog.py b/website/doctype/blog/blog.py index cfc0ca643e..2e96f4868b 100644 --- a/website/doctype/blog/blog.py +++ b/website/doctype/blog/blog.py @@ -27,6 +27,9 @@ class DocType: from website.utils import page_name self.doc.name = page_name(self.doc.title) + def validate(self): + self.doc.blog_intro = self.doc.blog_intro[:140] + def on_update(self): from website.utils import update_page_name update_page_name(self.doc, self.doc.title) @@ -66,6 +69,10 @@ class DocType: self.doc.full_name = get_fullname(self.doc.owner) self.doc.updated = global_date_format(self.doc.creation) self.doc.content_html = self.doc.content + if self.doc.blogger: + self.doc.blogger_info = webnotes.doc("blogger", self.doc.blogger).fields + if self.doc.blogger_info.avatar and not "/" in self.doc.blogger_info.avatar: + self.doc.blogger_info.avatar = "files/" + self.doc.blogger_info.avatar comment_list = webnotes.conn.sql("""\ select comment, comment_by_fullname, creation diff --git a/website/doctype/blog/blog.txt b/website/doctype/blog/blog.txt index af9606a71b..cdc7018f1e 100644 --- a/website/doctype/blog/blog.txt +++ b/website/doctype/blog/blog.txt @@ -2,7 +2,7 @@ { "creation": "2013-01-25 11:35:09", "docstatus": 0, - "modified": "2013-02-21 16:54:04", + "modified": "2013-03-07 16:32:13", "modified_by": "Administrator", "owner": "Administrator" }, @@ -50,18 +50,37 @@ "label": "Published" }, { - "description": "Description for listing page, in plain text, only a couple of lines.", + "doctype": "DocField", + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "doctype": "DocField", + "fieldname": "blogger", + "fieldtype": "Link", + "label": "Blogger", + "options": "Blogger", + "reqd": 1 + }, + { + "doctype": "DocField", + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "description": "Description for listing page, in plain text, only a couple of lines. (max 140 characters)", "doctype": "DocField", "fieldname": "blog_intro", "fieldtype": "Small Text", - "label": "Blog Intro" + "label": "Blog Intro", + "reqd": 1 }, { "doctype": "DocField", "fieldname": "content", "fieldtype": "Text Editor", "label": "Content", - "reqd": 0 + "reqd": 1 }, { "doctype": "DocField", diff --git a/website/doctype/blogger/__init__.py b/website/doctype/blogger/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/website/doctype/blogger/blogger.py b/website/doctype/blogger/blogger.py new file mode 100644 index 0000000000..28d0f29b3e --- /dev/null +++ b/website/doctype/blogger/blogger.py @@ -0,0 +1,18 @@ +# For license information, please see license.txt + +from __future__ import unicode_literals +import webnotes + +class DocType: + def __init__(self, d, dl): + self.doc, self.doclist = d, dl + + def on_update(self): + "if profile is set, then update all older blogs" + if self.doc.profile: + for blog in webnotes.conn.sql_list("""select name from tabBlog where owner=%s + and ifnull(blogger,'')=''""", self.doc.profile): + b = webnotes.bean("Blog", blog) + b.blogger = self.doc.name + b.save() + diff --git a/website/doctype/blogger/blogger.txt b/website/doctype/blogger/blogger.txt new file mode 100644 index 0000000000..01e0ceae20 --- /dev/null +++ b/website/doctype/blogger/blogger.txt @@ -0,0 +1,95 @@ +[ + { + "creation": "2013-03-07 16:28:19", + "docstatus": 0, + "modified": "2013-03-07 16:33:37", + "modified_by": "Administrator", + "owner": "Administrator" + }, + { + "allow_attach": 1, + "autoname": "field:short_name", + "description": "Profile of a Blogger", + "doctype": "DocType", + "document_type": "Master", + "max_attachments": 1, + "module": "Website", + "name": "__common__" + }, + { + "doctype": "DocField", + "name": "__common__", + "parent": "Blogger", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0 + }, + { + "doctype": "DocPerm", + "name": "__common__", + "parent": "Blogger", + "parentfield": "permissions", + "parenttype": "DocType", + "permlevel": 0, + "read": 1, + "write": 1 + }, + { + "doctype": "DocType", + "name": "Blogger" + }, + { + "description": "Will be used in url (usually first name).", + "doctype": "DocField", + "fieldname": "short_name", + "fieldtype": "Data", + "label": "Short Name", + "reqd": 1 + }, + { + "doctype": "DocField", + "fieldname": "full_name", + "fieldtype": "Data", + "label": "Full Name", + "reqd": 1 + }, + { + "doctype": "DocField", + "fieldname": "profile", + "fieldtype": "Link", + "label": "Profile", + "options": "Profile" + }, + { + "doctype": "DocField", + "fieldname": "bio", + "fieldtype": "Small Text", + "label": "Bio" + }, + { + "doctype": "DocField", + "fieldname": "avatar", + "fieldtype": "Select", + "label": "Avatar", + "options": "attach_files:" + }, + { + "doctype": "DocField", + "fieldname": "file_list", + "fieldtype": "Text", + "hidden": 1, + "label": "File List", + "no_copy": 1, + "print_hide": 1 + }, + { + "create": 1, + "doctype": "DocPerm", + "role": "Website Manager" + }, + { + "doctype": "DocPerm", + "match": "owner:profile", + "role": "Blogger" + } +] \ No newline at end of file diff --git a/website/doctype/style_settings/custom_template.css b/website/doctype/style_settings/custom_template.css index 52897447bd..d5283ffbce 100644 --- a/website/doctype/style_settings/custom_template.css +++ b/website/doctype/style_settings/custom_template.css @@ -1,3 +1,7 @@ +{% if doc.at_import %} +{{ doc.at_import }} +{% endif %} + body { {% if doc.background_image %} background: url("../files/{{ doc.background_image }}") repeat; @@ -7,8 +11,8 @@ body { {% else %} background-color: #edede7; {% endif %} -{% if doc.font %} - font-family: '{{ doc.font }}', Verdana, Sans !important; +{% if doc.font or doc.google_web_font_for_text %} + font-family: '{{ doc.google_web_font_for_text or doc.font }}', Verdana, Sans !important; {% endif %} {% if doc.font_size %} font-size: {{ doc.font_size }} !important; @@ -21,14 +25,32 @@ body { } {% endif %} -{% if doc.heading_font %} +div.outer { + background-color: #{{ doc.page_background or "fff" }}; +} + +{% if doc.google_web_font_for_heading or doc.heading_font %} h1, h2, h3, h4, h5 { - font-family: '{{ doc.heading_font}}', Arial, 'Helvetica Neue' !important; + font-family: '{{ doc.google_web_font_for_heading or doc.heading_font }}', Arial, 'Helvetica Neue' !important; } {% endif %} -/* Bootstrap Navbar */ +{% if doc.page_border %} +/* Page Border*/ +div.outer { + -moz-box-shadow: 0px 0px 3px rgba(0,0,0,0.9); + -webkit-box-shadow: 0px 0px 3px rgba(0,0,0,0.9); + box-shadow: 0px 0px 3px rgba(0,0,0,0.9); + border-radius: 5px; +} +{% else %} +div.web-footer { + border-top: 1px solid #eee; + padding-top: 10px; +} +{% endif %} +/* Bootstrap Navbar */ .navbar-inverse .navbar-inner { background-color: #{{ doc.top_bar_background or "444"}}; background-repeat: repeat-x; diff --git a/website/doctype/style_settings/style_settings.js b/website/doctype/style_settings/style_settings.js index 6a17db99ee..e8ee908799 100644 --- a/website/doctype/style_settings/style_settings.js +++ b/website/doctype/style_settings/style_settings.js @@ -18,6 +18,7 @@ cur_frm.cscript.onload_post_render = function() { wn.require('lib/public/js/lib/jscolor/jscolor.js'); cur_frm.fields_dict.background_color.input.className = 'color'; + cur_frm.fields_dict.page_background.input.className = 'color'; cur_frm.fields_dict.top_bar_background.input.className = 'color'; jscolor.bind(); } \ No newline at end of file diff --git a/website/doctype/style_settings/style_settings.py b/website/doctype/style_settings/style_settings.py index 51799483d4..95bba74182 100644 --- a/website/doctype/style_settings/style_settings.py +++ b/website/doctype/style_settings/style_settings.py @@ -29,10 +29,7 @@ class DocType: 'custom_template.css'), 'r') as f: temp = Template(f.read()) - if not self.doc.font_size: - self.doc.font_size = '13px' - - self.doc.small_font_size = str(int(self.doc.font_size[:-2])-2) + 'px' + self.prepare() self.doc.custom_css = temp.render(doc = self.doc) if self.doc.add_css: @@ -41,7 +38,30 @@ class DocType: from webnotes.sessions import clear_cache clear_cache('Guest') - del self.doc.fields['small_font_size'] + for f in ["small_font_size", "at_import"]: + if f in self.doc.fields: + del self.doc.fields[f] + + def prepare(self): + if not self.doc.font_size: + self.doc.font_size = '13px' + + self.doc.small_font_size = str(int(self.doc.font_size[:-2])-2) + 'px' + self.doc.page_border = int(self.doc.page_border) + + fonts = [] + if self.doc.google_web_font_for_heading: + fonts.append(self.doc.google_web_font_for_heading) + if self.doc.google_web_font_for_text: + fonts.append(self.doc.google_web_font_for_text) + + fonts = list(set(fonts)) + + self.doc.at_import = "" + for f in fonts: + self.doc.at_import += "\n@import url(http://fonts.googleapis.com/css?family=%s);" % f.replace(" ", "+") + + def on_update(self): """rebuild pages""" diff --git a/website/doctype/style_settings/style_settings.txt b/website/doctype/style_settings/style_settings.txt index 0063ba975e..ebdc35a181 100644 --- a/website/doctype/style_settings/style_settings.txt +++ b/website/doctype/style_settings/style_settings.txt @@ -2,7 +2,7 @@ { "creation": "2013-01-25 11:35:10", "docstatus": 0, - "modified": "2013-03-07 14:46:51", + "modified": "2013-03-07 16:06:22", "modified_by": "Administrator", "owner": "Administrator" }, @@ -38,6 +38,12 @@ "doctype": "DocType", "name": "Style Settings" }, + { + "doctype": "DocField", + "fieldname": "color", + "fieldtype": "Section Break", + "label": "Color" + }, { "description": "If image is selected, color will be ignored (attach first)", "doctype": "DocField", @@ -53,6 +59,25 @@ "fieldtype": "Data", "label": "Background Color" }, + { + "doctype": "DocField", + "fieldname": "page_background", + "fieldtype": "Data", + "label": "Page Background" + }, + { + "doctype": "DocField", + "fieldname": "page_border", + "fieldtype": "Check", + "label": "Page Border" + }, + { + "doctype": "DocField", + "fieldname": "cb0", + "fieldtype": "Column Break", + "print_width": "50%", + "width": "50%" + }, { "doctype": "DocField", "fieldname": "top_bar_background", @@ -69,31 +94,49 @@ }, { "doctype": "DocField", - "fieldname": "cb0", - "fieldtype": "Column Break", - "print_width": "50%", - "width": "50%" + "fieldname": "fonts", + "fieldtype": "Section Break", + "label": "Fonts" + }, + { + "doctype": "DocField", + "fieldname": "heading_font", + "fieldtype": "Select", + "label": "Font (Heading)", + "options": "\nHelvetica Neue\nLucida Grande\nVerdana\nArial\nGeorgia\nTahoma\nLato\nOpen Sans" }, { "doctype": "DocField", "fieldname": "font", "fieldtype": "Select", - "label": "Font", + "label": "Font (Text)", "options": "\nHelvetica Neue\nLucida Grande\nVerdana\nArial\nGeorgia\nTahoma" }, { "doctype": "DocField", "fieldname": "font_size", "fieldtype": "Select", - "label": "Font Size", + "label": "Font Size (Text)", "options": "\n12px\n13px\n14px\n15px\n16px" }, { "doctype": "DocField", - "fieldname": "heading_font", - "fieldtype": "Select", - "label": "Heading Font", - "options": "\nHelvetica Neue\nLucida Grande\nVerdana\nArial\nGeorgia\nTahoma\nLato\nOpen Sans" + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "description": "Add the name of Google Web Font e.g. \"Open Sans\"", + "doctype": "DocField", + "fieldname": "google_web_font_for_heading", + "fieldtype": "Data", + "label": "Google Web Font (Heading)" + }, + { + "description": "Add the name of Google Web Font e.g. \"Open Sans\"", + "doctype": "DocField", + "fieldname": "google_web_font_for_text", + "fieldtype": "Data", + "label": "Google Web Font (Text)" }, { "doctype": "DocField", diff --git a/website/helpers/blog.py b/website/helpers/blog.py index 2bff6e5708..dcb15cbd17 100644 --- a/website/helpers/blog.py +++ b/website/helpers/blog.py @@ -6,39 +6,39 @@ import webnotes import website.utils @webnotes.whitelist(allow_guest=True) -def get_blog_list(args=None): - """ - args = { - 'start': 0, - } - """ +def get_blog_list(start=0, by=None): import webnotes - - if not args: args = webnotes.form_dict - + condition = "" + if by: + condition = " and t1.blogger='%s'" % by.replace("'", "\'") query = """\ select - name, page_name, content, owner, creation as creation, - title, (select count(name) from `tabComment` where - comment_doctype='Blog' and comment_docname=`tabBlog`.name) as comments - from `tabBlog` - where ifnull(published,0)=1 + t1.title, t1.name, t1.page_name, t1.creation as creation, + ifnull(t1.blog_intro, t1.content) as content, + t2.full_name, t2.avatar, t1.blogger, + (select count(name) from `tabComment` where + comment_doctype='Blog' and comment_docname=t1.name) as comments + from `tabBlog` t1, `tabBlogger` t2 + where ifnull(t1.published,0)=1 + and t1.blogger = t2.name + %(condition)s order by creation desc, name asc - limit %s, 5""" % args.start + limit %(start)s, 5""" % {"start": start, "condition": condition} - result = webnotes.conn.sql(query, args, as_dict=1) + result = webnotes.conn.sql(query, as_dict=1) # strip html tags from content import webnotes.utils for res in result: from webnotes.utils import global_date_format, get_fullname - res['full_name'] = get_fullname(res['owner']) res['published'] = global_date_format(res['creation']) if not res['content']: res['content'] = website.utils.get_html(res['page_name']) - res['content'] = split_blog_content(res['content']) - + res['content'] = res['content'][:140] + if res.avatar and not "/" in res.avatar: + res.avatar = "files/" + res.avatar + return result @webnotes.whitelist(allow_guest=True) @@ -115,10 +115,4 @@ def get_blog_content(blog_page_name): import webnotes.utils content = webnotes.utils.escape_html(content) return content - -def split_blog_content(content): - content = content.split("") - content = len(content) > 1 and content[1] or content[0] - content = content.split("") - content = content[0] - return content \ No newline at end of file + \ No newline at end of file diff --git a/website/page/website_home/website_home.js b/website/page/website_home/website_home.js index c6b2253df1..d770586070 100644 --- a/website/page/website_home/website_home.js +++ b/website/page/website_home/website_home.js @@ -21,6 +21,11 @@ wn.module_page["Website"] = [ description: wn._("Embed image slideshows in website pages."), doctype:"Website Slideshow" }, + { + label: wn._("Blogger"), + description: wn._("Profile of a blog writer."), + doctype:"Blogger" + }, ] }, { diff --git a/website/templates/html/blog_page.html b/website/templates/html/blog_page.html index 24dd8d7acc..6f7796b0b6 100644 --- a/website/templates/html/blog_page.html +++ b/website/templates/html/blog_page.html @@ -13,17 +13,33 @@

{{ title }}

-
By {{ full_name }} on {{ updated }}
+
+ {{ blogger_info and blogger_info.full_name or full_name }} wrote this on {{ updated }}

{{ content_html }} - + {% if blogger_info %} +
+
+
+
+ +
+
+
+

{{ blogger_info.full_name }}

+

{{ blogger_info.bio }}

+

+ All posts by {{ blogger_info.full_name }}

+
+
+ {% endif %}

Comments


{% if not comment_list %} -
+

Be the first one to comment

{% endif %} @@ -32,22 +48,19 @@
+
+ Show posts by everyone

{% endblock %} \ No newline at end of file diff --git a/website/templates/html/outer.html b/website/templates/html/outer.html index a8b73ad122..ab6a69eb23 100644 --- a/website/templates/html/outer.html +++ b/website/templates/html/outer.html @@ -10,7 +10,7 @@