Merge branch 'master' of github.com:webnotes/erpnext

This commit is contained in:
Nabin Hait 2013-03-19 12:02:13 +05:30
commit 928355ad9a
29 changed files with 617 additions and 184 deletions

View File

@ -109,11 +109,11 @@ erpnext.AccountsChart = Class.extend({
},
onrender: function(treenode) {
if (ctype == 'Account') {
var bal = treenode.data && treenode.data.balance.split(' ') || ['',''];
if (bal && flt(bal[1])) {
if (ctype == 'Account' && treenode.data) {
if(treenode.data.balance) {
treenode.parent.append('<span class="balance-area">'
+ format_currency(bal[1], bal[0]) + '</span>');
+ format_currency(treenode.data.balance, treenode.data.currency)
+ '</span>');
}
}
}

View File

@ -1,7 +1,7 @@
from __future__ import unicode_literals
import webnotes
import webnotes.defaults
from webnotes.utils import flt
from accounts.utils import get_balance_on
@webnotes.whitelist()
@ -53,6 +53,7 @@ def get_children():
currency = webnotes.conn.sql("select default_currency from `tabCompany` where name = %s", company)[0][0]
for each in acc:
bal = get_balance_on(each.get("value"))
each['balance'] = currency + ' ' + str(bal or 0)
each["currency"] = currency
each["balance"] = flt(bal)
return acc

View File

@ -24,6 +24,9 @@ from utilities.transaction_base import TransactionBase
class AccountsController(TransactionBase):
def get_gl_dict(self, args, cancel=None):
"""this method populates the common properties of a gl entry record"""
if cancel is None:
cancel = (self.doc.docstatus == 2)
gl_dict = {
'company': self.doc.company,
'posting_date': self.doc.posting_date,
@ -31,7 +34,7 @@ class AccountsController(TransactionBase):
'voucher_no': self.doc.name,
'aging_date': self.doc.fields.get("aging_date") or self.doc.posting_date,
'remarks': self.doc.remarks,
'is_cancelled': self.doc.docstatus == 2 and "Yes" or "No",
'is_cancelled': cancel and "Yes" or "No",
'fiscal_year': self.doc.fiscal_year,
'debit': 0,
'credit': 0,

View File

@ -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()

View File

@ -0,0 +1,29 @@
import webnotes
def execute():
# delete wrong gle entries created due to a bug in make_gl_entries of Account Controller
# when using payment reconciliation
res = webnotes.conn.sql_list("""select distinct gl1.voucher_no
from `tabGL Entry` gl1, `tabGL Entry` gl2
where
date(gl1.modified) >= "2013-03-11"
and date(gl1.modified) = date(gl2.modified)
and gl1.voucher_no = gl2.voucher_no
and gl1.voucher_type = "Journal Voucher"
and gl1.voucher_type = gl2.voucher_type
and gl1.posting_date = gl2.posting_date
and gl1.account = gl2.account
and ifnull(gl1.is_cancelled, 'No') = 'No' and ifnull(gl2.is_cancelled, 'No') = 'No'
and ifnull(gl1.against_voucher, '') = ifnull(gl2.against_voucher, '')
and ifnull(gl1.against_voucher_type, '') = ifnull(gl2.against_voucher_type, '')
and gl1.remarks = gl2.remarks
and ifnull(gl1.debit, 0) = ifnull(gl2.credit, 0)
and ifnull(gl1.credit, 0) = ifnull(gl2.debit, 0)
and gl1.name > gl2.name""")
for r in res:
webnotes.conn.sql("""update `tabGL Entry` set `is_cancelled`='Yes'
where voucher_type='Journal Voucher' and voucher_no=%s""", r)
jv = webnotes.bean("Journal Voucher", r)
jv.run_method("make_gl_entries")

View File

@ -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",
@ -211,6 +210,6 @@ patch_list = [
"execute:webnotes.delete_doc('DocType', 'Attendance Control Panel')",
"patches.march_2013.p02_get_global_default",
"patches.march_2013.p03_rename_blog_to_blog_post",
"execute:webnotes.bean('Style Settings', 'Style Settings').save()",
"execute:webnotes.reload_doc('hr', 'search_criteria', 'monthly_attendance_details')",
"patches.march_2013.p05_payment_reconciliation",
]

View File

@ -166,22 +166,24 @@ pscript.feature_dict = {
'Address': {'fields':['sales_partner']},
'Contact': {'fields':['sales_partner']},
'Customer': {'fields':['sales_team']},
'Delivery Note': {'fields':['sales_team','Packing List']},
'Delivery Note': {'fields':['sales_team','packing_list']},
'Item': {'fields':['item_customer_details']},
'Sales Invoice': {'fields':['sales_team']},
'Sales Order': {'fields':['sales_team','Packing List']}
'Sales Invoice': {'fields':['sales_team', 'packing_list']},
'Sales Order': {'fields':['sales_team','packing_list']}
},
'fs_more_info': {
'Delivery Note': {'fields':['More Info']},
'Opportunity': {'fields':['More Info']},
'Material Request': {'fields':['More Info']},
'Lead': {'fields':['More Info']},
'Purchase Invoice': {'fields':['More Info']},
'Purchase Order': {'fields':['More Info']},
'Purchase Receipt': {'fields':['More Info']},
'Quotation': {'fields':['More Info']},
'Sales Invoice': {'fields':['More Info']},
'Sales Order': {'fields':['More Info']},
"Customer Issue": {"fields": ["more_info"]},
'Material Request': {'fields':['more_info']},
'Lead': {'fields':['more_info']},
'Opportunity': {'fields':['more_info']},
'Purchase Invoice': {'fields':['more_info']},
'Purchase Order': {'fields':['more_info']},
'Purchase Receipt': {'fields':['more_info']},
'Supplier Quotation': {'fields':['more_info']},
'Quotation': {'fields':['more_info']},
'Sales Invoice': {'fields':['more_info']},
'Sales Order': {'fields':['more_info']},
'Delivery Note': {'fields':['more_info']},
},
'fs_quality': {
'Item': {'fields':['Item Inspection Criteria','inspection_required']},
@ -199,25 +201,23 @@ pscript.feature_dict = {
}
$(document).bind('form_refresh', function() {
for(sys_feat in sys_defaults)
{
if(sys_defaults[sys_feat]=='0' && (sys_feat in pscript.feature_dict)) //"Features to hide" exists
{
if(cur_frm.doc.doctype in pscript.feature_dict[sys_feat])
{
for(fort in pscript.feature_dict[sys_feat][cur_frm.doc.doctype])
{
if(fort=='fields')
for(sys_feat in sys_defaults) {
if(sys_defaults[sys_feat]=='0'
&& (sys_feat in pscript.feature_dict)) { //"Features to hide" exists
if(cur_frm.doc.doctype in pscript.feature_dict[sys_feat]) {
for(fort in pscript.feature_dict[sys_feat][cur_frm.doc.doctype]) {
if(fort=='fields') {
hide_field(pscript.feature_dict[sys_feat][cur_frm.doc.doctype][fort]);
else if(cur_frm.fields_dict[fort])
{
} else if(cur_frm.fields_dict[fort]) {
for(grid_field in pscript.feature_dict[sys_feat][cur_frm.doc.doctype][fort])
cur_frm.fields_dict[fort].grid.set_column_disp(pscript.feature_dict[sys_feat][cur_frm.doc.doctype][fort][grid_field], false);
}
else
} else {
msgprint('Grid "'+fort+'" does not exists');
}
}
}
}
}
})

View File

@ -97,7 +97,6 @@ class DocType(SellingController):
return webnotes.conn.get_value('Sales Email Settings',None,'email_id')
def on_trash(self):
webnotes.conn.sql("""delete from tabCommunication where lead=%s""",
self.doc.name)
webnotes.conn.sql("""update tabCommunication set lead=null where lead=%s""", self.doc.name)
webnotes.conn.sql("""update `tabSupport Ticket` set lead='' where lead=%s""",
self.doc.name)

View File

@ -1,6 +1,17 @@
# SETUP:
# install pip install --upgrade dropbox
#
# Create new Dropbox App
#
# in conf.py, set oauth2 settings
# dropbox_access_key
# dropbox_access_secret
import os
import webnotes
from webnotes.utils import get_request_site_address
from webnotes.utils import get_request_site_address, get_base_path
from webnotes import _
@webnotes.whitelist()
def get_dropbox_authorize_url():
@ -10,7 +21,7 @@ def get_dropbox_authorize_url():
+ "?cmd=setup.doctype.backup_manager.backup_dropbox.dropbox_callback"
url = sess.build_authorize_url(request_token, return_address)
return {
"url": url,
"key": request_token.key,
@ -19,43 +30,43 @@ def get_dropbox_authorize_url():
@webnotes.whitelist(allow_guest=True)
def dropbox_callback(oauth_token=None, not_approved=False):
from dropbox import client
if not not_approved:
if webnotes.conn.get_value("Backup Manager", None, "dropbox_access_key")==oauth_token:
webnotes.conn.set_value("Backup Manager", "Backup Manager", "dropbox_access_allowed", 1)
allowed = 1
message = "Dropbox access allowed."
sess = get_dropbox_session()
sess.set_request_token(webnotes.conn.get_value("Backup Manager", None, "dropbox_access_key"),
webnotes.conn.get_value("Backup Manager", None, "dropbox_access_secret"))
access_token = sess.obtain_access_token()
webnotes.conn.set_value("Backup Manager", "Backup Manager", "dropbox_access_key", access_token.key)
webnotes.conn.set_value("Backup Manager", "Backup Manager", "dropbox_access_secret", access_token.secret)
webnotes.conn.set_value("Backup Manager", "Backup Manager", "dropbox_access_allowed", allowed)
dropbox_client = client.DropboxClient(sess)
dropbox_client.file_create_folder("files")
webnotes.conn.set_value("Backup Manager", "Backup Manager", "dropbox_access_key",
access_token.key)
webnotes.conn.set_value("Backup Manager", "Backup Manager", "dropbox_access_secret",
access_token.secret)
else:
webnotes.conn.set_value("Backup Manager", "Backup Manager", "dropbox_access_allowed", 0)
allowed = 0
message = "Illegal Access Token Please try again."
else:
webnotes.conn.set_value("Backup Manager", "Backup Manager", "dropbox_access_allowed", 0)
allowed = 0
message = "Dropbox Access not approved."
webnotes.message_title = "Dropbox Approval"
webnotes.message = "<h3>%s</h3><p>Please close this window.</p>" % message
webnotes.conn.commit()
webnotes.response['type'] = 'page'
webnotes.response['page_name'] = 'message.html'
def backup_to_dropbox():
from dropbox import client, session
from dropbox import client, session, rest
from conf import dropbox_access_key, dropbox_secret_key
from webnotes.utils.backups import new_backup
if not webnotes.conn:
webnotes.connect()
sess = session.DropboxSession(dropbox_access_key, dropbox_secret_key, "app_folder")
sess.set_token(webnotes.conn.get_value("Backup Manager", None, "dropbox_access_key"),
@ -65,24 +76,23 @@ def backup_to_dropbox():
# upload database
backup = new_backup()
filename = backup.backup_path_db
filename = os.path.join(get_base_path(), "public", "backups",
os.path.basename(backup.backup_path_db))
upload_file_to_dropbox(filename, "database", dropbox_client)
# upload files
response = dropbox_client.metadata("files")
response = dropbox_client.metadata("/files")
# add missing files
for filename in os.listdir(os.path.join("public", "files")):
# upload files to files folder
filename = os.path.join(get_base_path(), "public", "files")
for filename in os.listdir(filename):
found = False
for file_metadata in response["contents"]:
if filename==os.path.basename(file_metadata["path"]):
if os.stat(os.path.join("public", "files", filename)).st_size==file_metadata["bytes"]:
if os.stat(filename).st_size==file_metadata["bytes"]:
found=True
if not found:
upload_file_to_dropbox(os.path.join("public", "files", filename), "files", dropbox_client)
upload_file_to_dropbox(os.path.join(get_base_path(),"public", "files", filename), "files", dropbox_client)
def get_dropbox_session():
from dropbox import session
@ -91,21 +101,18 @@ def get_dropbox_session():
except ImportError, e:
webnotes.msgprint(_("Please set Dropbox access keys in") + " conf.py",
raise_exception=True)
sess = session.DropboxSession(dropbox_access_key, dropbox_secret_key, "app_folder")
return sess
def upload_file_to_dropbox(filename, folder, dropbox_client):
if __name__=="__main__":
print "Uploading " + filename
size = os.stat(filename).st_size
f = open(filename,'r')
if size > 4194304:
uploader = dropbox_client.get_chunked_uploader(f, size)
while uploader.offset < size:
try:
uploader.upload_chunked()
finish(folder + '/' + os.path.basename(filename), overwrite='True')
except rest.ErrorResponse, e:
pass
else:

View File

@ -0,0 +1,161 @@
# SETUP:
# install pip install --upgrade google-api-python-client
#
# In Google API
# - create new API project
# - create new oauth2 client (create installed app type as google \
# does not support subdomains)
#
# in conf.py, set oauth2 settings
# gdrive_client_id
# gdrive_client_secret
import httplib2
import sys
import os
import mimetypes
import webnotes
import oauth2client.client
from webnotes.utils import get_request_site_address, get_base_path
from webnotes import _, msgprint
from apiclient.discovery import build
from apiclient.http import MediaFileUpload
@webnotes.whitelist()
def get_gdrive_authorize_url():
flow = get_gdrive_flow()
authorize_url = flow.step1_get_authorize_url()
return {
"authorize_url": authorize_url,
}
@webnotes.whitelist()
def upload_files(name, mimetype, service, folder_id):
if not webnotes.conn:
webnotes.connect()
file_name = os.path.basename(name)
media_body = MediaFileUpload(name, mimetype=mimetype, resumable=True)
body = {
'title': file_name,
'description': 'Backup File',
'mimetype': mimetype,
'parents': [{
'kind': 'drive#filelink',
'id': folder_id
}]
}
request = service.files().insert(body=body, media_body=media_body)
response = None
while response is None:
status, response = request.next_chunk()
def backup_to_gdrive():
from webnotes.utils.backups import new_backup
found_database = False
found_files = False
if not webnotes.conn:
webnotes.connect()
flow = get_gdrive_flow()
credentials_json = webnotes.conn.get_value("Backup Manager", None, "gdrive_credentials")
credentials = oauth2client.client.Credentials.new_from_json(credentials_json)
http = httplib2.Http()
http = credentials.authorize(http)
drive_service = build('drive', 'v2', http=http)
# upload database
backup = new_backup()
path = os.path.join(get_base_path(), "public", "backups")
filename = os.path.join(path, os.path.basename(backup.backup_path_db))
# upload files to database folder
upload_files(filename, 'application/x-gzip', drive_service,
webnotes.conn.get_value("Backup Manager", None, "database_folder_id"))
# upload files to files folder
path = os.path.join(get_base_path(), "public", "files")
for files in os.listdir(path):
filename = path + "/" + files
ext = filename.split('.')[-1]
size = os.path.getsize(filename)
if ext == 'gz' or ext == 'gzip':
mimetype = 'application/x-gzip'
else:
mimetype = mimetypes.types_map["." + ext]
#Compare Local File with Server File
param = {}
children = drive_service.children().list(
folderId=webnotes.conn.get_value("Backup Manager", None, "files_folder_id"),
**param).execute()
for child in children.get('items', []):
file = drive_service.files().get(fileId=child['id']).execute()
if files == file['title'] and size == int(file['fileSize']):
found_files = True
break
if not found_files:
upload_files(filename, mimetype, drive_service, webnotes.conn.get_value("Backup Manager", None, "files_folder_id"))
def get_gdrive_flow():
from oauth2client.client import OAuth2WebServerFlow
import conf
if not hasattr(conf, "gdrive_client_id"):
webnotes.msgprint(_("Please set Google Drive access keys in") + " conf.py",
raise_exception=True)
#callback_url = get_request_site_address(True) \
# + "?cmd=setup.doctype.backup_manager.backup_googledrive.googledrive_callback"
# for installed apps since google does not support subdomains
redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
flow = OAuth2WebServerFlow(conf.gdrive_client_id, conf.gdrive_client_secret,
"https://www.googleapis.com/auth/drive", redirect_uri)
return flow
@webnotes.whitelist()
def gdrive_callback(verification_code = None):
flow = get_gdrive_flow()
if verification_code:
credentials = flow.step2_exchange(verification_code)
allowed = 1
# make folders to save id
http = httplib2.Http()
http = credentials.authorize(http)
drive_service = build('drive', 'v2', http=http)
erpnext_folder_id = create_erpnext_folder(drive_service)
database_folder_id = create_folder('database', drive_service, erpnext_folder_id)
files_folder_id = create_folder('files', drive_service, erpnext_folder_id)
webnotes.conn.set_value("Backup Manager", "Backup Manager", "gdrive_access_allowed", allowed)
webnotes.conn.set_value("Backup Manager", "Backup Manager", "database_folder_id", database_folder_id)
webnotes.conn.set_value("Backup Manager", "Backup Manager", "files_folder_id", files_folder_id)
final_credentials = credentials.to_json()
webnotes.conn.set_value("Backup Manager", "Backup Manager", "gdrive_credentials", final_credentials)
webnotes.msgprint("Updated")
def create_erpnext_folder(service):
if not webnotes.conn:
webnotes.connect()
erpnext = {
'title': 'erpnext',
'mimeType': 'application/vnd.google-apps.folder'
}
erpnext = service.files().insert(body=erpnext).execute()
return erpnext['id']
def create_folder(name, service, folder_id):
database = {
'title': name,
'mimeType': 'application/vnd.google-apps.folder',
'parents': [{
'kind': 'drive#fileLink',
'id': folder_id
}]
}
database = service.files().insert(body=database).execute()
return database['id']
if __name__=="__main__":
backup_to_gdrive()

View File

@ -1,24 +1,65 @@
cur_frm.cscript.refresh = function(doc) {
cur_frm.disable_save();
}
//dropbox
cur_frm.cscript.allow_dropbox_access = function(doc) {
wn.call({
method: "setup.doctype.backup_manager.backup_dropbox.get_dropbox_authorize_url",
callback: function(r) {
if(!r.exc) {
cur_frm.set_value("dropbox_access_secret", r.message.secret);
cur_frm.set_value("dropbox_access_key", r.message.key);
cur_frm.save(null, function() {
window.open(r.message.url);
});
if (doc.send_notifications_to == '') {
msgprint("Please enter email address.")
}
else {
wn.call({
method: "setup.doctype.backup_manager.backup_dropbox.get_dropbox_authorize_url",
callback: function(r) {
if(!r.exc) {
cur_frm.set_value("dropbox_access_secret", r.message.secret);
cur_frm.set_value("dropbox_access_key", r.message.key);
cur_frm.save(null, function() {
window.open(r.message.url);
});
}
}
}
})
})
}
}
cur_frm.cscript.backup_right_now = function(doc) {
msgprint("Backing up and uploading. This may take a few minutes.")
wn.call({
method: "setup.doctype.backup_manager.backup_manager.take_backups",
method: "setup.doctype.backup_manager.backup_manager.take_backups_dropbox",
callback: function(r) {
msgprint("Backups taken. Please check your email for the response.")
}
})
}
}
//gdrive
cur_frm.cscript.allow_gdrive_access = function(doc) {
if (doc.send_notifications_to == '') {
msgprint("Please enter email address.")
}
else {
wn.call({
method: "setup.doctype.backup_manager.backup_googledrive.get_gdrive_authorize_url",
callback: function(r) {
window.open(r.message.authorize_url);
}
})
}
}
cur_frm.cscript.validate_gdrive = function(doc) {
wn.call({
method: "setup.doctype.backup_manager.backup_manager.gdrive_callback",
args: {
verification_code: doc.verification_code
},
});
}
cur_frm.cscript.upload_backups_to_dropbox = function(doc) {
cur_frm.save()
}
cur_frm.cscript.upload_backups_to_gdrive = function(doc) {
cur_frm.save()
}

View File

@ -3,6 +3,8 @@
from __future__ import unicode_literals
import webnotes
from webnotes import _
from backup_dropbox import dropbox_callback, get_dropbox_session, get_dropbox_authorize_url
from backup_googledrive import gdrive_callback, get_gdrive_flow, get_gdrive_authorize_url
class DocType:
def __init__(self, d, dl):
@ -16,10 +18,13 @@ def take_backups_weekly():
def take_backups_if(freq):
if webnotes.conn.get_value("Backup Manager", None, "upload_backups_to_dropbox")==freq:
take_backups()
take_backups_dropbox()
if webnotes.conn.get_value("Backup Manager", None, "upload_backups_to_gdrive")==freq:
take_backups_gdrive()
@webnotes.whitelist()
def take_backups():
def take_backups_dropbox():
try:
from setup.doctype.backup_manager.backup_dropbox import backup_to_dropbox
backup_to_dropbox()
@ -27,6 +32,16 @@ def take_backups():
except Exception, e:
send_email(False, "Dropbox", e)
#backup to gdrive
@webnotes.whitelist()
def take_backups_gdrive():
try:
from setup.doctype.backup_manager.backup_googledrive import backup_to_gdrive
backup_to_gdrive()
send_email(True, "Google Drive")
except Exception, e:
send_email(False, "Google Drive", e)
def send_email(success, service_name, error_status=None):
if success:
subject = "Backup Upload Successful"
@ -44,5 +59,5 @@ def send_email(success, service_name, error_status=None):
# email system managers
from webnotes.utils.email_lib import sendmail
sendmail(webnotes.conn.get_value("Backup Manager", None, "send_notifications_to").split(","),
subject=subject, msg=message)
sendmail(webnotes.conn.get_value("Backup Manager", None, "send_notifications_to").split(","),
subject=subject, msg=message)

View File

@ -1,8 +1,8 @@
[
{
"creation": "2013-03-05 16:35:50",
"creation": "2013-03-15 11:06:59",
"docstatus": 0,
"modified": "2013-03-07 12:18:07",
"modified": "2013-03-15 17:27:33",
"modified_by": "Administrator",
"owner": "Administrator"
},
@ -56,7 +56,9 @@
"doctype": "DocField",
"fieldname": "backup_right_now",
"fieldtype": "Button",
"label": "Backup Right Now"
"hidden": 1,
"label": "Backup Right Now",
"read_only": 1
},
{
"description": "Note: Backups and files are not deleted from Dropbox, you will have to delete them manually.",
@ -102,6 +104,70 @@
"fieldtype": "Button",
"label": "Allow Dropbox Access"
},
{
"description": "Note: Backups and files are not deleted from Google Drive, you will have to delete them manually.",
"doctype": "DocField",
"fieldname": "sync_with_gdrive",
"fieldtype": "Section Break",
"label": "Sync with Google Drive"
},
{
"doctype": "DocField",
"fieldname": "upload_backups_to_gdrive",
"fieldtype": "Select",
"label": "Upload Backups to Google Drive",
"options": "Never\nDaily\nWeekly"
},
{
"doctype": "DocField",
"fieldname": "allow_gdrive_access",
"fieldtype": "Button",
"label": "Allow Google Drive Access"
},
{
"doctype": "DocField",
"fieldname": "verification_code",
"fieldtype": "Data",
"label": "Enter Verification Code"
},
{
"doctype": "DocField",
"fieldname": "validate_gdrive",
"fieldtype": "Button",
"label": "Validate"
},
{
"doctype": "DocField",
"fieldname": "gdrive_access_allowed",
"fieldtype": "Check",
"hidden": 1,
"label": "Google Drive Access Allowed",
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "gdrive_credentials",
"fieldtype": "Text",
"hidden": 1,
"label": "Credentials",
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "database_folder_id",
"fieldtype": "Data",
"hidden": 1,
"label": "Database Folder ID",
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "files_folder_id",
"fieldtype": "Data",
"hidden": 1,
"label": "Files Folder ID",
"read_only": 1
},
{
"doctype": "DocPerm"
}

View File

@ -45,7 +45,7 @@ wn.module_page["Setup"] = [
},
{
"doctype":"Workflow",
label:wn._("Workfow"),
label:wn._("Workflow"),
"description":wn._("Set workflow rules.")
},
{

View File

@ -238,8 +238,7 @@ class DocType(DocListController):
from website.helpers.product import get_parent_item_groups, url_for_website
self.parent_groups = get_parent_item_groups(self.doc.item_group) + [{"name":self.doc.name}]
self.doc.website_image = url_for_website(self.doc.website_image)
self.doc.title = self.doc.item_name == self.doc.name and self.doc.item_name or \
(self.doc.item_name + " [" + self.doc.name + "]")
self.doc.title = self.doc.item_name
if self.doc.slideshow:
from website.helpers.slideshow import get_slideshow

View File

@ -105,6 +105,9 @@ class DocType(BuyingController):
import utilities
utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped",
"Cancelled"])
# restrict material request type
self.validate_value("material_request_type", "in", ["Purchase", "Transfer"])
# Get Purchase Common Obj
pc_obj = get_obj(dt='Purchase Common')

View File

@ -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": "<div class=\"alert\">Link for About Us Page is \"about.html\"</div>"
"options": "<div class=\"alert\">Link for About Us Page is \"/about\"</div>"
},
{
"description": "Introduce your company to the website visitor.",

View File

@ -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": "<div class=\"alert\">Link for Contact Page is \"contact.html\"</div>"
"options": "<div class=\"alert\">Link for Contact Page is \"/contact\"</div>"
},
{
"description": "Address to be displayed on the Contact Page",

View File

@ -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 {
@ -47,37 +47,51 @@ div.outer {
{% 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;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
-webkibox-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
}
{% else %}
{% if doc.background_color == doc.page_background %}
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 %}
{% endif %}
div.web-footer, div.web-footer a {
font-size: 90%;
color: #{{ get_hex_shade(doc.background_color or "ffffff", 70) }};
}
/* 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;
}
.navbar-inner {
box-shadow: none;
}
{% if doc.top_bar_background == doc.page_background %}.navbar-inner {
border-bottom: 1px solid #{{ get_hex_shade(doc.page_background or "ffffff", 15) }};
}{% endif %}
.navbar-inverse .brand,
.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 +100,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 +117,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 +140,103 @@ 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", 10) }};
}
.breadcrumb > li {
text-shadow: none;
}
.breadcrumb > li > .divider {
color: #{{ doc.page_text }};
}
.breadcrumb > .active {
color: #{{ doc.page_text }};
}
.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) }};
}

View File

@ -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

View File

@ -2,7 +2,7 @@
{
"creation": "2013-03-08 11:36:53",
"docstatus": 0,
"modified": "2013-03-12 13:35:14",
"modified": "2013-03-14 11:57:20",
"modified_by": "Administrator",
"owner": "Administrator"
},
@ -150,14 +150,14 @@
"fieldtype": "Column Break"
},
{
"description": "Add the name of Google Web Font e.g. \"Open Sans\"",
"description": "Add the name of <a href=\"http://google.com/webfonts\" target=\"_blank\">Google Web Font</a> 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\"",
"description": "Add the name of <a href=\"http://google.com/webfonts\" target=\"_blank\">Google Web Font</a> e.g. \"Open Sans\"",
"doctype": "DocField",
"fieldname": "google_web_font_for_text",
"fieldtype": "Data",

View File

@ -41,4 +41,14 @@ $.extend(cur_frm.cscript, {
this.fieldobj.refresh_options(get_parent_options('top_bar_items'));
});
}
});
});
cur_frm.cscript.set_banner_from_image = function(doc) {
if(!doc.banner_image) {
msgprint(wn._("Select a Banner Image first."));
}
var src = doc.banner_image;
if(src.indexOf("/")==-1) src = "files/" + src;
cur_frm.set_value("banner_html", "<a href='/'><img src='"+ src
+"' style='max-width: 200px;'></a>");
}

View File

@ -2,7 +2,7 @@
{
"creation": "2013-03-07 11:55:11",
"docstatus": 0,
"modified": "2013-03-12 11:17:11",
"modified": "2013-03-13 16:25:22",
"modified_by": "Administrator",
"owner": "Administrator"
},
@ -78,6 +78,20 @@
"fieldtype": "Section Break",
"label": "Banner"
},
{
"description": "Select an image of approx width 150px with a transparent background for best results.",
"doctype": "DocField",
"fieldname": "banner_image",
"fieldtype": "Select",
"label": "Banner Image",
"options": "attach_files:"
},
{
"doctype": "DocField",
"fieldname": "set_banner_from_image",
"fieldtype": "Button",
"label": "Set Banner from Image"
},
{
"description": "Banner is above the Top Menu Bar.",
"doctype": "DocField",

View File

@ -44,7 +44,7 @@ rss_item = u"""
<description>%(content)s</description>
<link>%(link)s</link>
<guid>%(name)s</guid>
<pubDate>%(creation)s</pubDate>
<pubDate>%(published_on)s</pubDate>
</item>"""
def generate():
@ -57,13 +57,12 @@ def generate():
items = ''
blog_list = webnotes.conn.sql("""\
select page_name as name, modified, creation, title from `tabBlog Post`
select page_name as name, published_on, modified, title, content from `tabBlog Post`
where ifnull(published,0)=1
order by creation desc, modified desc, name asc limit 20""", as_dict=1)
order by published_on desc limit 20""", as_dict=1)
for blog in blog_list:
blog.link = host + '/' + blog.name + '.html'
blog.content = get_blog_content(blog.name)
items += rss_item % blog

View File

@ -6,6 +6,7 @@
.layout-wrapper {
background-color: #fff;
color: #333;
padding: 10px;
box-shadow: 1px 1px 3px 3px #ccc;
font-size: 12px;

View File

@ -1,6 +1,6 @@
<style>
.item-main-image {
max-width: 400px;
max-width: 100%;
margin: auto;
}
.web-long-description {

View File

@ -11,46 +11,42 @@
{% block content %}
{% include 'html/product_search_box.html' %}
{% include 'html/product_breadcrumbs.html' %}
<div class="span12">
<h3 itemprop="name">{{ item_name }}</h3>
<p class="help">Item Code: {{ name }}</p>
</div>
<div class="span12 product-page-content" itemscope itemtype="http://schema.org/Product">
{% if slideshow %}
{% include "html/slideshow.html" %}
{% else %}
{% if website_image %}
<image itemprop="image" class="item-main-image"
src="{{ website_image }}" />
{% else %}
<div class="img-area">
{% include 'html/product_missing_image.html' %}
</div>
{% endif %}
{% endif %}
<br><br>
<div class="row">
<div class="span9">
<h3>Product Description</h3>
<div class="span6">
{% if slideshow %}
{% include "html/slideshow.html" %}
{% else %}
{% if website_image %}
<image itemprop="image" class="item-main-image"
src="{{ website_image }}" />
{% else %}
<div class="img-area">
{% include 'html/product_missing_image.html' %}
</div>
{% endif %}
{% endif %}
</div>
<div class="span6">
<h3 itemprop="name" style="margin-top: 0px;">{{ item_name }}</h3>
<p class="help">Item Code: {{ name }}</p>
<h4>Product Description</h4>
<div itemprop="description">
{{ web_long_description or web_short_description or
"[No description given]" }}
</div>
<hr>
{% if obj.doclist.get({"doctype":"Item Website Specification"}) %}
<h3>Specifications</h3>
<table class="table table-bordered" style="width: 100%">
{% for d in obj.doclist.get(
{"doctype":"Item Website Specification"}) %}
<tr>
<td style="width: 30%;">{{ d.label }}</td>
<td>{{ d.description }}</td>
</tr>
{% endfor %}
</table>
<h4>Specifications</h4>
<table class="table table-bordered" style="width: 100%">
{% for d in obj.doclist.get(
{"doctype":"Item Website Specification"}) %}
<tr>
<td style="width: 30%;">{{ d.label }}</td>
<td>{{ d.description }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>
<div class="span3">
<div class="item-price hide">
<p>Price:</p>
</div>

View File

@ -9,7 +9,7 @@
<h3>{{ obj.doc.company_history_heading or "Company History" }}</h3>
{% for d in obj.doclist.get({"doctype":"Company History"}) %}
<div class="row">
<span class="span2" style="text-align: right"><h4 style="margin:0px;">{{ d.year }}</h4></span>
<span class="span2"><h4 style="margin:0px;">{{ d.year }}</h4></span>
<span class="span10"><p>{{ d.highlight }}</p></span>
</div>
{% endfor %}
@ -20,11 +20,11 @@
<div class="row" itemscope itemtype="http://schema.org/Person">
<span class="span2">
<div class="avatar avatar-large">
<img class="avatar" src="{{ d.image }}" style="" itemprop="image">
<img class="avatar" src="{{ d.image_link }}" style="" itemprop="image">
</div>
</span>
<span class="span10"><h4 itemprop="name">{{ d.full_name }}</h4>
<div itemprop="description">{{ d.bio }}</div>
<p itemprop="description">{{ d.bio }}</p>
</span>
</div>
{% endfor %}

View File

@ -46,6 +46,8 @@ page_settings_map = {
"writers": "website.helpers.blog.get_writers_args"
}
no_cache = "message"
def render(page_name):
"""render html page"""
try:
@ -68,10 +70,12 @@ def get_html(page_name):
# load from cache, if auto cache clear is falsy
if not (hasattr(conf, 'auto_cache_clear') and conf.auto_cache_clear or 0):
html = webnotes.cache().get_value("page:" + page_name)
from_cache = True
if not page_name in no_cache:
html = webnotes.cache().get_value("page:" + page_name)
from_cache = True
if not html:
webnotes.connect()
html = load_into_cache(page_name)
from_cache = False
@ -302,4 +306,32 @@ def url_for_website(url):
if url and not url.lower().startswith("http"):
return "files/" + url
else:
return url
return url
def get_hex_shade(color, percent):
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]
avg = (float(int(r, 16) + int(g, 16) + int(b, 16)) / 3)
# switch dark and light shades
if avg > 128:
percent = -percent
# stronger diff for darker shades
if percent < 25 and avg < 64:
percent = percent * 2
return p(r) + p(g) + p(b)