From 6a4c4682190aea8fbec8e8a15d9a7de672f205db Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 12 Mar 2013 14:59:46 +0530 Subject: [PATCH 1/5] fix in about --- setup/page/setup/setup.js | 2 +- website/doctype/about_us_settings/about_us_settings.txt | 4 ++-- website/doctype/contact_us_settings/contact_us_settings.txt | 6 +++--- website/templates/pages/about.html | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/setup/page/setup/setup.js b/setup/page/setup/setup.js index 394a5f6b56..a9df4592f9 100644 --- a/setup/page/setup/setup.js +++ b/setup/page/setup/setup.js @@ -45,7 +45,7 @@ wn.module_page["Setup"] = [ }, { "doctype":"Workflow", - label:wn._("Workfow"), + label:wn._("Workflow"), "description":wn._("Set workflow rules.") }, { diff --git a/website/doctype/about_us_settings/about_us_settings.txt b/website/doctype/about_us_settings/about_us_settings.txt index 6a27f732b9..b846d2bc6c 100644 --- a/website/doctype/about_us_settings/about_us_settings.txt +++ b/website/doctype/about_us_settings/about_us_settings.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 15:53:15", "docstatus": 0, - "modified": "2013-03-12 13:51:29", + "modified": "2013-03-12 14:48:34", "modified_by": "Administrator", "owner": "Administrator" }, @@ -46,7 +46,7 @@ "fieldname": "help", "fieldtype": "HTML", "label": "Help", - "options": "
Link for About Us Page is \"about.html\"
" + "options": "
Link for About Us Page is \"/about\"
" }, { "description": "Introduce your company to the website visitor.", diff --git a/website/doctype/contact_us_settings/contact_us_settings.txt b/website/doctype/contact_us_settings/contact_us_settings.txt index 1bc7b05713..ef2da02af7 100644 --- a/website/doctype/contact_us_settings/contact_us_settings.txt +++ b/website/doctype/contact_us_settings/contact_us_settings.txt @@ -1,8 +1,8 @@ [ { - "creation": "2012-12-27 19:04:50", + "creation": "2013-02-21 20:12:42", "docstatus": 0, - "modified": "2013-02-21 16:49:33", + "modified": "2013-03-12 14:49:01", "modified_by": "Administrator", "owner": "Administrator" }, @@ -44,7 +44,7 @@ "fieldname": "help", "fieldtype": "HTML", "label": "Help", - "options": "
Link for Contact Page is \"contact.html\"
" + "options": "
Link for Contact Page is \"/contact\"
" }, { "description": "Address to be displayed on the Contact Page", diff --git a/website/templates/pages/about.html b/website/templates/pages/about.html index 28fb1f6a25..0015d894d2 100644 --- a/website/templates/pages/about.html +++ b/website/templates/pages/about.html @@ -20,7 +20,7 @@
- +

{{ d.full_name }}

From 84afb351d970c3b044807b104c16a815bc806f18 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 12 Mar 2013 15:20:46 +0530 Subject: [PATCH 2/5] fix in about --- website/templates/pages/about.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/templates/pages/about.html b/website/templates/pages/about.html index 0015d894d2..291f39ce49 100644 --- a/website/templates/pages/about.html +++ b/website/templates/pages/about.html @@ -24,7 +24,7 @@

{{ d.full_name }}

-
{{ d.bio }}
+

{{ d.bio }}

{% endfor %} From 2eddd8ad682a62a5d584e1d49b47fb673b4eee3d Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 12 Mar 2013 21:39:56 +0530 Subject: [PATCH 3/5] removed december_2012.clear_web_cache patch --- patches/december_2012/clear_web_cache.py | 13 ------------- patches/patch_list.py | 1 - website/templates/pages/about.html | 2 +- 3 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 patches/december_2012/clear_web_cache.py diff --git a/patches/december_2012/clear_web_cache.py b/patches/december_2012/clear_web_cache.py deleted file mode 100644 index b92f4bde08..0000000000 --- a/patches/december_2012/clear_web_cache.py +++ /dev/null @@ -1,13 +0,0 @@ -import webnotes -def execute(): - webnotes.reload_doc("website", "doctype", "web_page") - webnotes.reload_doc("website", "doctype", "blog") - webnotes.reload_doc("stock", "doctype", "item") - webnotes.reload_doc("setup", "doctype", "item_group") - - # build wn-web.js and wn-web.css - from website.helpers.make_web_include_files import make - make() - - import website.utils - website.utils.clear_cache() \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index 5a504f8a51..3f51209768 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -132,7 +132,6 @@ patch_list = [ "patches.december_2012.fix_default_print_format", "patches.december_2012.file_list_rename", "patches.december_2012.replace_createlocal", - "patches.december_2012.clear_web_cache", "patches.december_2012.remove_quotation_next_contact", "patches.december_2012.stock_entry_cleanup", "patches.december_2012.production_order_naming_series", diff --git a/website/templates/pages/about.html b/website/templates/pages/about.html index 291f39ce49..380c5439ec 100644 --- a/website/templates/pages/about.html +++ b/website/templates/pages/about.html @@ -9,7 +9,7 @@

{{ obj.doc.company_history_heading or "Company History" }}

{% for d in obj.doclist.get({"doctype":"Company History"}) %}
-

{{ d.year }}

+

{{ d.year }}

{{ d.highlight }}

{% endfor %} From 4ceb1b51645b2991339a3b56a0dbab079d5068c0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 13 Mar 2013 11:40:30 +0530 Subject: [PATCH 4/5] aii: gl entries for stock entry and testcases --- stock/doctype/stock_entry/stock_entry.py | 48 +++++++- stock/doctype/stock_entry/test_stock_entry.py | 106 +++++++++++++----- 2 files changed, 124 insertions(+), 30 deletions(-) diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 74b71d97b3..cd20266d6b 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -39,7 +39,6 @@ class DocType(AccountsController): def validate(self): self.validate_purpose() - self.validate_serial_nos() pro_obj = self.doc.production_order and \ get_obj('Production Order', self.doc.production_order) or None @@ -51,22 +50,20 @@ class DocType(AccountsController): self.validate_incoming_rate() self.validate_bom() self.validate_finished_goods() - self.validate_return_reference_doc() - self.validate_with_material_request() def on_submit(self): self.update_serial_no(1) self.update_stock_ledger(0) - # update Production Order self.update_production_order(1) + self.make_gl_entries() def on_cancel(self): self.update_serial_no(0) self.update_stock_ledger(1) - # update Production Order self.update_production_order(0) + self.make_gl_entries() def validate_purpose(self): valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer", @@ -167,6 +164,47 @@ class DocType(AccountsController): elif self.doc.purpose != "Material Transfer": self.doc.production_order = None + def make_gl_entries(self): + if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")): + return + + abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr") + stock_in_hand_account = self.get_stock_in_hand_account() + total_valuation_amount = self.get_total_valuation_amount() + + if total_valuation_amount: + gl_entries = [ + # debit stock in hand account + self.get_gl_dict({ + "account": stock_in_hand_account, + "against": "Stock Adjustment - %s" % abbr, + "debit": total_valuation_amount, + "remarks": self.doc.remarks or "Accounting Entry for Stock", + }, self.doc.docstatus == 2), + + # debit stock received but not billed account + self.get_gl_dict({ + "account": "Stock Adjustment - %s" % abbr, + "against": stock_in_hand_account, + "credit": total_valuation_amount, + "cost_center": "Auto Inventory Accounting - %s" % abbr, + "remarks": self.doc.remarks or "Accounting Entry for Stock", + }, self.doc.docstatus == 2), + ] + from accounts.general_ledger import make_gl_entries + make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2) + + def get_total_valuation_amount(self): + total_valuation_amount = 0 + for item in self.doclist.get({"parentfield": "mtn_details"}): + if item.t_warehouse and not item.s_warehouse: + total_valuation_amount += flt(item.incoming_rate) * flt(item.transfer_qty) + + if item.s_warehouse and not item.t_warehouse: + total_valuation_amount -= flt(item.incoming_rate) * flt(item.transfer_qty) + + return total_valuation_amount + def get_stock_and_rate(self): """get stock and incoming rate on posting date""" for d in getlist(self.doclist, 'mtn_details'): diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index 21d15f7e23..a4103c37bb 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -43,6 +43,20 @@ class TestStockEntry(unittest.TestCase): ]) ) + mr.cancel() + self.check_stock_ledger_entries("Stock Entry", mr.doc.name, + sorted([["_Test Item", "_Test Warehouse", 50.0], + ["_Test Item", "_Test Warehouse", -50.0]])) + + self.check_gl_entries("Stock Entry", mr.doc.name, + sorted([ + [stock_in_hand_account, 5000.0, 0.0], + ["Stock Adjustment - _TC", 0.0, 5000.0], + [stock_in_hand_account, 0.0, 5000.0], + ["Stock Adjustment - _TC", 5000.0, 0.0] + ]) + ) + webnotes.defaults.set_global_default("auto_inventory_accounting", 0) def test_material_issue_gl_entry(self): @@ -53,25 +67,78 @@ class TestStockEntry(unittest.TestCase): mr.insert() mr.submit() + mi = webnotes.bean(copy=test_records[1]) + mi.insert() + mi.submit() + stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company", "stock_in_hand_account") - self.check_stock_ledger_entries("Stock Entry", mr.doc.name, - [["_Test Item", "_Test Warehouse", 50.0]]) + self.check_stock_ledger_entries("Stock Entry", mi.doc.name, + [["_Test Item", "_Test Warehouse", -40.0]]) - self.check_gl_entries("Stock Entry", mr.doc.name, + self.check_gl_entries("Stock Entry", mi.doc.name, sorted([ - [stock_in_hand_account, 5000.0, 0.0], - ["Stock Adjustment - _TC", 0.0, 5000.0] + [stock_in_hand_account, 0.0, 4000.0], + ["Stock Adjustment - _TC", 4000.0, 0.0] ]) ) + mi.cancel() + + self.check_stock_ledger_entries("Stock Entry", mi.doc.name, + sorted([["_Test Item", "_Test Warehouse", -40.0], + ["_Test Item", "_Test Warehouse", 40.0]])) + + self.check_gl_entries("Stock Entry", mi.doc.name, + sorted([ + [stock_in_hand_account, 0.0, 4000.0], + ["Stock Adjustment - _TC", 4000.0, 0.0], + [stock_in_hand_account, 4000.0, 0.0], + ["Stock Adjustment - _TC", 0.0, 4000.0], + ]) + ) + + webnotes.defaults.set_global_default("auto_inventory_accounting", 0) + + def test_material_transfer_gl_entry(self): + webnotes.conn.sql("delete from `tabStock Ledger Entry`") + webnotes.defaults.set_global_default("auto_inventory_accounting", 1) + + mr = webnotes.bean(copy=test_records[0]) + mr.insert() + mr.submit() + + mtn = webnotes.bean(copy=test_records[2]) + mtn.insert() + mtn.submit() + + self.check_stock_ledger_entries("Stock Entry", mtn.doc.name, + [["_Test Item", "_Test Warehouse", -45.0], ["_Test Item", "_Test Warehouse 1", 45.0]]) + + # no gl entry + gl_entries = webnotes.conn.sql("""select * from `tabGL Entry` + where voucher_type = 'Stock Entry' and voucher_no=%s""", mtn.doc.name) + self.assertFalse(gl_entries) + + mtn.cancel() + self.check_stock_ledger_entries("Stock Entry", mtn.doc.name, + sorted([["_Test Item", "_Test Warehouse", 45.0], + ["_Test Item", "_Test Warehouse 1", -45.0], + ["_Test Item", "_Test Warehouse", -45.0], + ["_Test Item", "_Test Warehouse 1", 45.0]])) + + # no gl entry + gl_entries = webnotes.conn.sql("""select * from `tabGL Entry` + where voucher_type = 'Stock Entry' and voucher_no=%s""", mtn.doc.name) + self.assertFalse(gl_entries) + webnotes.defaults.set_global_default("auto_inventory_accounting", 0) def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle): # check stock ledger entries - sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry` - where voucher_type = %s and voucher_no = %s order by item_code, warehouse""", + sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry` where voucher_type = %s + and voucher_no = %s order by item_code, warehouse, actual_qty""", (voucher_type, voucher_no), as_dict=1) self.assertTrue(sle) @@ -87,7 +154,6 @@ class TestStockEntry(unittest.TestCase): from `tabGL Entry` where voucher_type=%s and voucher_no=%s order by account asc, debit asc""", (voucher_type, voucher_no), as_dict=1) self.assertTrue(gl_entries) - for i, gle in enumerate(gl_entries): self.assertEquals(expected_gl_entries[i][0], gle.account) self.assertEquals(expected_gl_entries[i][1], gle.debit) @@ -100,7 +166,8 @@ test_records = [ "doctype": "Stock Entry", "posting_date": "2013-01-25", "posting_time": "17:14:24", - "purpose": "Material Receipt" + "purpose": "Material Receipt", + "fiscal_year": "_Test Fiscal Year 2013", }, { "conversion_factor": 1.0, @@ -121,7 +188,8 @@ test_records = [ "doctype": "Stock Entry", "posting_date": "2013-01-25", "posting_time": "17:15", - "purpose": "Material Issue" + "purpose": "Material Issue", + "fiscal_year": "_Test Fiscal Year 2013", }, { "conversion_factor": 1.0, @@ -142,12 +210,13 @@ test_records = [ "doctype": "Stock Entry", "posting_date": "2013-01-25", "posting_time": "17:14:24", - "purpose": "Material Transfer" + "purpose": "Material Transfer", + "fiscal_year": "_Test Fiscal Year 2013", }, { "conversion_factor": 1.0, "doctype": "Stock Entry Detail", - "item_code": "_Test Item Home Desktop 100", + "item_code": "_Test Item", "parentfield": "mtn_details", "incoming_rate": 100, "qty": 45.0, @@ -156,19 +225,6 @@ test_records = [ "uom": "_Test UOM", "s_warehouse": "_Test Warehouse", "t_warehouse": "_Test Warehouse 1", - }, - { - "conversion_factor": 1.0, - "doctype": "Stock Entry Detail", - "item_code": "_Test Item Home Desktop 100", - "parentfield": "mtn_details", - "qty": 45.0, - "incoming_rate": 100, - "stock_uom": "_Test UOM", - "transfer_qty": 45.0, - "uom": "_Test UOM", - "s_warehouse": "_Test Warehouse", - "t_warehouse": "_Test Warehouse 1", } ] ] \ No newline at end of file From 860f3ceada9ca4b4fdd7308449b30e34dd67838d Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 13 Mar 2013 12:50:08 +0530 Subject: [PATCH 5/5] added shades to website style --- .../style_settings/custom_template.css | 91 ++++++++++++++----- .../doctype/style_settings/style_settings.py | 3 +- website/utils.py | 25 ++++- 3 files changed, 93 insertions(+), 26 deletions(-) diff --git a/website/doctype/style_settings/custom_template.css b/website/doctype/style_settings/custom_template.css index 24353f97e0..208c7dd30a 100644 --- a/website/doctype/style_settings/custom_template.css +++ b/website/doctype/style_settings/custom_template.css @@ -29,7 +29,7 @@ body { {% endif %} div.outer { - background-color: #{{ doc.page_background or "fff" }}; + background-color: #{{ doc.page_background or "fffffff" }}; } {% if doc.google_web_font_for_heading or doc.heading_font %}h1, h2, h3, h4, h5 { @@ -54,14 +54,14 @@ div.outer { } {% else %} div.web-footer { - border-top: 1px solid #eee; + border-top: 1px solid #{{ get_hex_shade(doc.page_background or "ffffff", 15) }}; padding-top: 10px; } {% endif %} /* Bootstrap Navbar */ .navbar-inverse .navbar-inner { - background-color: #{{ doc.top_bar_background or "444"}}; + background-color: #{{ doc.top_bar_background or "444444"}}; background-repeat: repeat-x; border-color: transparent; background-image: none; @@ -71,13 +71,13 @@ div.web-footer { .navbar-inverse .brand:hover, .navbar-inverse .brand:focus, .navbar-inverse .nav > li > a { - color: #{{ doc.top_bar_foreground or "fff"}}; + color: #{{ doc.top_bar_foreground or "fffffff"}}; text-shadow: none; } .navbar-inverse .nav > li > a:hover, .navbar-inverse .nav > li > a:focus { - color: #{{ doc.top_bar_background or "000"}}; + color: #{{ doc.top_bar_background or "0000000"}}; } .navbar-inverse .navbar-text { @@ -86,14 +86,14 @@ div.web-footer { .navbar-inverse .nav > li > a:focus, .navbar-inverse .nav > li > a:hover { - color: #{{ doc.top_bar_foreground or "fff"}}; + color: #{{ doc.top_bar_foreground or "fffffff"}}; 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"}}; + color: #{{ doc.top_bar_foreground or "fffffff"}}; background-color: transparent; } @@ -103,7 +103,7 @@ div.web-footer { .navbar-inverse .navbar-link:hover, .navbar-inverse .navbar-link:focus { - color: #{{ doc.top_bar_foreground or "fff"}}; + color: #{{ doc.top_bar_foreground or "fffffff"}}; } .navbar-fixed-top .navbar-inner, @@ -126,47 +126,90 @@ div.web-footer { .navbar-inverse .nav li.dropdown > .dropdown-toggle .caret, .navbar-inverse .nav li.dropdown > .dropdown-toggle:hover .caret { - border-top-color: #{{ doc.top_bar_foreground or "fff"}}; - border-bottom-color: #{{ doc.top_bar_foreground or "fff"}}; + border-top-color: #{{ doc.top_bar_foreground or "fffffff"}}; + border-bottom-color: #{{ doc.top_bar_foreground or "fffffff"}}; } .navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, .navbar-inverse .nav li.dropdown.open > .dropdown-toggle:hover .caret { - border-top-color: #{{ doc.top_bar_background or "000"}}; - border-bottom-color: #{{ doc.top_bar_background or "000"}}; + border-top-color: #{{ doc.top_bar_background or "0000000"}}; + border-bottom-color: #{{ doc.top_bar_background or "0000000"}}; } .navbar-inverse .nav li.dropdown.open > .dropdown-toggle { - color: #{{ doc.top_bar_background or "000"}}; - background-color: #{{ doc.top_bar_foreground or "fff"}}; + color: #{{ doc.top_bar_background or "0000000"}}; + background-color: #{{ doc.top_bar_foreground or "fffffff"}}; } @media (max-width: 800px) { .navbar-inverse .nav-collapse .nav > li > a, .navbar-inverse .nav-collapse .dropdown-menu a { - background-color: #{{ doc.top_bar_background or "000"}}; - color: #{{ doc.top_bar_foreground or "fff"}}; + background-color: #{{ doc.top_bar_background or "0000000"}}; + color: #{{ doc.top_bar_foreground or "fffffff"}}; } .navbar-inverse .nav-collapse .nav > li > a:hover, .navbar-inverse .nav-collapse .dropdown-menu a:hover { - background-color: #{{ doc.top_bar_foreground or "fff"}}; - color: #{{ doc.top_bar_background or "000"}}; + background-color: #{{ doc.top_bar_foreground or "fffffff"}}; + color: #{{ doc.top_bar_background or "0000000"}}; } .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" }}; + border-top-color: #{{ doc.top_bar_foreground or "fffffff" }}; + border-bottom-color: #{{ doc.top_bar_foreground or "fffffff" }}; } .navbar-inverse .nav li.dropdown > .dropdown-toggle:hover .caret { - border-top-color: #{{ doc.top_bar_background or "000" }}; - border-bottom-color: #{{ doc.top_bar_background or "000" }}; + border-top-color: #{{ doc.top_bar_background or "0000000" }}; + border-bottom-color: #{{ doc.top_bar_background or "0000000" }}; } .navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, .navbar-inverse .nav li.dropdown.open > .dropdown-toggle:hover .caret { - border-top-color: #{{ doc.top_bar_background or "000" }}; - border-bottom-color: #{{ doc.top_bar_background or "000" }}; + border-top-color: #{{ doc.top_bar_background or "0000000" }}; + border-bottom-color: #{{ doc.top_bar_background or "0000000" }}; } } + +.breadcrumb { + background-color: #{{ get_hex_shade(doc.page_background or "ffffff", 5) }}; +} + +.table-striped tbody > tr:nth-child(odd) > td, +.table-striped tbody > tr:nth-child(odd) > th { + background-color: #{{ get_hex_shade(doc.page_background or "ffffff", 5) }}; +} + +.table-hover tbody tr:hover td, +.table-hover tbody tr:hover th { + background-color: #{{ get_hex_shade(doc.page_background or "ffffff", 10) }}; +} + +.table-bordered { + border: 1px solid #{{ get_hex_shade(doc.page_background or "ffffff", 15) }}; +} + +.table th, +.table td { + border-top: 1px solid #{{ get_hex_shade(doc.page_background or "ffffff", 15) }}; +} + +.table-bordered th, +.table-bordered td { + border-left: 1px solid #{{ get_hex_shade(doc.page_background or "ffffff", 15) }}; +} + + + +.hero-unit { + background-color: #{{ get_hex_shade(doc.page_background or "ffffff", 15) }}; +} + +pre, code { + background-color: #{{ get_hex_shade(doc.page_background or "ffffff", 5) }}; +} + +hr { + border-top: 1px solid #{{ get_hex_shade(doc.page_background or "ffffff", 15) }}; + border-bottom: 1px solid #{{ get_hex_shade(doc.page_background or "ffffff", 5) }}; +} diff --git a/website/doctype/style_settings/style_settings.py b/website/doctype/style_settings/style_settings.py index 91ca0a579e..1cc3467d78 100644 --- a/website/doctype/style_settings/style_settings.py +++ b/website/doctype/style_settings/style_settings.py @@ -27,6 +27,7 @@ class DocType: def validate(self): """make custom css""" from jinja2 import Template + from website.utils import get_hex_shade import os self.validate_colors() @@ -38,7 +39,7 @@ class DocType: self.prepare() - self.doc.custom_css = temp.render(doc = self.doc) + self.doc.custom_css = temp.render(doc = self.doc, get_hex_shade=get_hex_shade) if self.doc.add_css: self.doc.custom_css += '\n\n/* User CSS */\n\n' + self.doc.add_css diff --git a/website/utils.py b/website/utils.py index 0f62d26b63..b38d2b31a9 100644 --- a/website/utils.py +++ b/website/utils.py @@ -302,4 +302,27 @@ def url_for_website(url): if url and not url.lower().startswith("http"): return "files/" + url else: - return url \ No newline at end of file + return url + +def get_hex_shade(color, percent): + # switch dark and light shades + if int(color, 16) > int("808080", 16): + percent = -percent + + # stronger diff for darker shades + if int(color, 16) < int("333333", 16): + percent = percent * 2 + + def p(c): + v = int(c, 16) + int(int('ff', 16) * (float(percent)/100)) + if v < 0: + v=0 + if v > 255: + v=255 + h = hex(v)[2:] + if len(h) < 2: + h = "0" + h + return h + + r, g, b = color[0:2], color[2:4], color[4:6] + return p(r) + p(g) + p(b) \ No newline at end of file