[FIX] ir_qweb: contact field must add 'http://' if the website url value has not any protocol
</group>
<group>
<field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '<>', 'view')]" name="account_id" on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)" groups="account.group_account_user"/>
- <field name="invoice_line_tax_id" context="{'type':parent.type}" domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]" widget="many2many_tags"/>
+ <field name="invoice_line_tax_id" context="{'type':parent.get('type')}" domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]" widget="many2many_tags"/>
<field domain="[('type','<>','view'), ('company_id', '=', parent.company_id)]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
</group>
dates_query += ' < %s)'
args_list += (form[str(i)]['stop'],)
args_list += (self.date_from,)
- self.cr.execute('''SELECT l.partner_id, SUM(l.debit-l.credit)
+ self.cr.execute('''SELECT l.partner_id, SUM(l.debit-l.credit), l.reconcile_partial_id
FROM account_move_line AS l, account_account, account_move am
WHERE (l.account_id = account_account.id) AND (l.move_id=am.id)
AND (am.state IN %s)
AND account_account.active
AND ''' + dates_query + '''
AND (l.date <= %s)
- GROUP BY l.partner_id''', args_list)
- t = self.cr.fetchall()
- d = {}
- for i in t:
- d[i[0]] = i[1]
- history.append(d)
+ GROUP BY l.partner_id, l.reconcile_partial_id''', args_list)
+ partners_partial = self.cr.fetchall()
+ partners_amount = dict((i[0],0) for i in partners_partial)
+ for partner_info in partners_partial:
+ if partner_info[2]:
+ # in case of partial reconciliation, we want to keep the left amount in the oldest period
+ self.cr.execute('''SELECT MIN(COALESCE(date_maturity,date)) FROM account_move_line WHERE reconcile_partial_id = %s''', (partner_info[2],))
+ date = self.cr.fetchall()
+ if date and args_list[-3] <= date[0][0] <= args_list[-2]:
+ # partial reconcilation
+ self.cr.execute('''SELECT SUM(l.debit-l.credit)
+ FROM account_move_line AS l
+ WHERE l.reconcile_partial_id = %s''', (partner_info[2],))
+ unreconciled_amount = self.cr.fetchall()
+ partners_amount[partner_info[0]] += unreconciled_amount[0][0]
+ else:
+ partners_amount[partner_info[0]] += partner_info[1]
+ history.append(partners_amount)
for partner in partners:
values = {}
if not name:
name = self.pool.get('product.product').name_get(cr, uid, [res.id], context=local_context)[0][1]
if res.description_sale:
- result['name'] += '\n'+res.description_sale
+ name += '\n'+res.description_sale
result.update({'name': name or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': price})
new_date = next_date+relativedelta(days=+interval)
elif contract.recurring_rule_type == 'weekly':
new_date = next_date+relativedelta(weeks=+interval)
+ elif contract.recurring_rule_type == 'yearly':
+ new_date = next_date+relativedelta(years=+interval)
else:
new_date = next_date+relativedelta(months=+interval)
self.write(cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, context=context)
<div attrs="{'invisible': [('recurring_invoices','=',False)]}">
<field name="recurring_invoice_line_ids">
<tree string="Account Analytic Lines" editable="bottom">
- <field name="product_id" on_change="product_id_change(product_id, uom_id, quantity, name, parent.partner_id, price_unit, parent.pricelist_id, parent.company_id)"/>
+ <field name="product_id" on_change="product_id_change(product_id, uom_id, quantity, False, parent.partner_id, False, parent.pricelist_id, parent.company_id)"/>
<field name="name"/>
<field name="quantity"/>
<field name="uom_id"/>
import datetime
from openerp.osv import fields, osv
+from openerp.tools import ustr
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
for line in self.browse(cr, uid, ids, context=context):
acc_ids = [x.id for x in line.general_budget_id.account_ids]
if not acc_ids:
- raise osv.except_osv(_('Error!'),_("The Budget '%s' has no accounts!") % str(line.general_budget_id.name))
+ raise osv.except_osv(_('Error!'),_("The Budget '%s' has no accounts!") % ustr(line.general_budget_id.name))
date_to = line.date_to
date_from = line.date_from
if context.has_key('wizard_date_from'):
--- /dev/null
+========================
+``base_gengo`` changelog
+========================
+
+*******************************
+saas-4 (backported from saas-5)
+*******************************
+
+- Library update: ``mygengo`` (https://pypi.python.org/pypi/mygengo/1.3.3) was outdated and has been replaced by ``gengo`` (https://pypi.python.org/pypi/gengo).
_logger = logging.getLogger(__name__)
try:
- from mygengo import MyGengo
+ from gengo import Gengo
except ImportError:
- _logger.warning('Gengo library not found, Gengo features disabled. If you plan to use it, please install the mygengo library from http://pypi.python.org/pypi/mygengo')
+ _logger.warning('Gengo library not found, Gengo features disabled. If you plan to use it, please install the gengo library from http://pypi.python.org/pypi/gengo')
GENGO_DEFAULT_LIMIT = 20
'sync_limit' : 20
}
def gengo_authentication(self, cr, uid, context=None):
- '''
+ '''
This method tries to open a connection with Gengo. For that, it uses the Public and Private
keys that are linked to the company (given by Gengo on subscription). It returns a tuple with
* as first element: a boolean depicting if the authentication was a success or not
- * as second element: the connection, if it was a success, or the error message returned by
+ * as second element: the connection, if it was a success, or the error message returned by
Gengo when the connection failed.
- This error message can either be displayed in the server logs (if the authentication was called
- by the cron) or in a dialog box (if requested by the user), thus it's important to return it
+ This error message can either be displayed in the server logs (if the authentication was called
+ by the cron) or in a dialog box (if requested by the user), thus it's important to return it
translated.
'''
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
if not user.company_id.gengo_public_key or not user.company_id.gengo_private_key:
return (False, _("Gengo `Public Key` or `Private Key` are missing. Enter your Gengo authentication parameters under `Settings > Companies > Gengo Parameters`."))
try:
- gengo = MyGengo(
+ gengo = Gengo(
public_key=user.company_id.gengo_public_key.encode('ascii'),
private_key=user.company_id.gengo_private_key.encode('ascii'),
sandbox=user.company_id.gengo_sandbox,
def _sync_request(self, cr, uid, limit=GENGO_DEFAULT_LIMIT, context=None):
"""
This scheduler will send a job request to the gengo , which terms are
- waiing to be translated and for which gengo_translation is enabled.
+ waiing to be translated and for which gengo_translation is enabled.
- A special key 'gengo_language' can be passed in the context in order to
- request only translations of that language only. Its value is the language
+ A special key 'gengo_language' can be passed in the context in order to
+ request only translations of that language only. Its value is the language
ID in openerp.
"""
if context is None:
total = 0
weight = 0
volume = 0
+ product_uom_obj = self.pool.get('product.uom')
for line in order.order_line:
if not line.product_id or line.is_delivery:
continue
- weight += (line.product_id.weight or 0.0) * line.product_uom_qty
- volume += (line.product_id.volume or 0.0) * line.product_uom_qty
+ q = product_uom_obj._compute_qty(cr, uid, line.product_uom.id, line.product_uos_qty, line.product_id.uom_id.id)
+ weight += (line.product_id.weight or 0.0) * q
+ volume += (line.product_id.volume or 0.0) * q
total = order.amount_total or 0.0
-
return self.get_price_from_picking(cr, uid, id, total,weight, volume, context=context)
def get_price_from_picking(self, cr, uid, id, total, weight, volume, context=None):
return int(department_ids[0][0])
return None
+ def _get_default_company_id(self, cr, uid, department_id=None, context=None):
+ company_id = False
+ if department_id:
+ department = self.pool['hr.department'].browse(cr, uid, department_id, context=context)
+ company_id = department.company_id.id if department and department.company_id else False
+ if not company_id:
+ company_id = self.pool['res.company']._company_default_get(cr, uid, 'hr.applicant', context=context)
+ return company_id
+
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
access_rights_uid = access_rights_uid or uid
stage_obj = self.pool.get('hr.recruitment.stage')
'user_id': lambda s, cr, uid, c: uid,
'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c),
- 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'hr.applicant', context=c),
+ 'company_id': lambda s, cr, uid, c: s._get_default_company_id(cr, uid, s._get_default_department_id(cr, uid, c), c),
'color': 0,
'date_last_stage_update': fields.datetime.now,
}
def message_get_default_recipients(self, cr, uid, ids, context=None):
if context and context.get('thread_model') and context['thread_model'] in self.pool and context['thread_model'] != self._name:
- sub_ctx = dict(context)
- sub_ctx.pop('thread_model')
- return self.pool[context['thread_model']].message_get_default_recipients(cr, uid, ids, context=sub_ctx)
+ if hasattr(self.pool[context['thread_model']], 'message_get_default_recipients'):
+ sub_ctx = dict(context)
+ sub_ctx.pop('thread_model')
+ return self.pool[context['thread_model']].message_get_default_recipients(cr, uid, ids, context=sub_ctx)
res = {}
for record in self.browse(cr, SUPERUSER_ID, ids, context=context):
recipient_ids, email_to, email_cc = set(), False, False
this.$('.oe_mail_expand').on('click', this.on_expand);
this.$('.oe_mail_reduce').on('click', this.on_expand);
this.$('.oe_mail_action_model').on('click', this.on_record_clicked);
+ this.$('.oe_mail_action_author').on('click', this.on_record_author_clicked);
},
on_record_clicked: function (event) {
+ event.preventDefault();
+ var self = this;
var state = {
'model': this.model,
'id': this.res_id,
'title': this.record_name
};
session.webclient.action_manager.do_push_state(state);
+ this.context.params = {
+ model: this.model,
+ res_id: this.res_id,
+ };
+ this.thread.ds_thread.call("message_redirect_action", {context: this.context}).then(function(action){
+ self.do_action(action);
+ });
+ },
+
+ on_record_author_clicked: function (event) {
+ event.preventDefault();
+ var partner_id = $(event.target).data('partner');
+ var state = {
+ 'model': 'res.partner',
+ 'id': partner_id,
+ 'title': this.record_name
+ };
+ session.webclient.action_manager.do_push_state(state);
+ var action = {
+ type:'ir.actions.act_window',
+ view_type: 'form',
+ view_mode: 'form',
+ res_model: 'res.partner',
+ views: [[false, 'form']],
+ res_id: partner_id,
+ }
+ this.do_action(action);
},
/* Call the on_compose_message on the thread of this message. */
<t t-if="widget.attachment_ids.length > 0">
<div class="oe_msg_attachment_list"></div>
</t>
- <a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}"><t t-esc="widget.author_id[2]"/></a>
+ <a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&id=#{widget.author_id[0]}" t-att-data-partner="widget.author_id[0]" class="oe_mail_action_author"><t t-esc="widget.author_id[2]"/></a>
<span t-if="widget.author_id and (!widget.options.show_link or !widget.author_id[0])"><t t-esc="widget.author_id[2]"/></span>
<t t-if="widget.type == 'notification'">
updated document
<t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and (widget.subtype or widget.partner_ids.length > 0))"
t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner">
<span t-attf-class="oe_partner_follower">
- <a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}"><t t-esc="partner[1]"/></a>
+ <a t-if="widget.options.show_link" t-attf-href="#model=res.partner&id=#{partner[0]}" t-att-data-partner="partner[0]" class="oe_mail_action_author"><t t-esc="partner[1]"/></a>
<t t-if="!widget.options.show_link" t-esc="partner[1]"/>
</span>
<t t-if="!partner_last">,</t>
from dateutil import relativedelta
import json
import random
-import urllib
-import urlparse
from openerp import tools
+from openerp.exceptions import Warning
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
from openerp.osv import osv, fields
rec_id = self.create(cr, uid, {'name': name, 'email': email}, context=context)
return self.name_get(cr, uid, [rec_id], context)[0]
+ def message_get_default_recipients(self, cr, uid, ids, context=None):
+ res = {}
+ for record in self.browse(cr, uid, ids, context=context):
+ res[record.id] = {'partner_ids': [], 'email_to': record.email, 'email_cc': False}
+ return res
+
class MassMailingList(osv.Model):
"""Model of a contact list. """
for mailing in self.browse(cr, uid, ids, context=context):
# instantiate an email composer + send emails
res_ids = self.get_recipients(cr, uid, mailing, context=context)
+ if not res_ids:
+ raise Warning('Please select recipients.')
comp_ctx = dict(context, active_ids=res_ids)
composer_values = {
'author_id': author_id,
<p style="border-left: 1px solid #8e0000; margin-left: 30px;">
<strong>REFERENCES</strong><br />
Order number: <strong>${object.name}</strong><br />
- Order total: <strong>${object.amount_total} ${object.pricelist_id.currency_id.name}</strong><br />
+ Order total: <strong>${object.amount_total} ${object.currency_id.name}</strong><br />
Order date: ${object.date_order}<br />
% if object.origin:
Order reference: ${object.origin}<br />
'picking_ids': fields.one2many('stock.picking.in', 'purchase_id', 'Picking List', readonly=True, help="This is the list of incoming shipments that have been generated for this purchase order."),
'shipped':fields.boolean('Received', readonly=True, select=True, help="It indicates that a picking has been done"),
'shipped_rate': fields.function(_shipped_rate, string='Received Ratio', type='float'),
- 'invoiced': fields.function(_invoiced, string='Invoice Received', type='boolean', help="It indicates that an invoice has been paid"),
+ 'invoiced': fields.function(_invoiced, string='Invoice Received', type='boolean', help="It indicates that an invoice has been validated"),
'invoiced_rate': fields.function(_invoiced_rate, string='Invoiced', type='float'),
'invoice_method': fields.selection([('manual','Based on Purchase Order lines'),('order','Based on generated draft invoice'),('picking','Based on incoming shipments')], 'Invoicing Control', required=True,
readonly=True, states={'draft':[('readonly',False)], 'sent':[('readonly',False)]},
"""Call webkit in order to generate pdf"""
if not webkit_header:
webkit_header = report_xml.webkit_header
- tmp_dir = tempfile.gettempdir()
- out_filename = tempfile.mktemp(suffix=".pdf", prefix="webkit.tmp.")
+ fd, out_filename = tempfile.mkstemp(suffix=".pdf",
+ prefix="webkit.tmp.")
file_to_del = [out_filename]
if comm_path:
command = [comm_path]
# default to UTF-8 encoding. Use <meta charset="latin-1"> to override.
command.extend(['--encoding', 'utf-8'])
if header :
- head_file = file( os.path.join(
- tmp_dir,
- str(time.time()) + '.head.html'
- ),
- 'w'
- )
- head_file.write(self._sanitize_html(header.encode('utf-8')))
- head_file.close()
+ with tempfile.NamedTemporaryFile(suffix=".head.html",
+ delete=False) as head_file:
+ head_file.write(self._sanitize_html(header.encode('utf-8')))
file_to_del.append(head_file.name)
command.extend(['--header-html', head_file.name])
if footer :
- foot_file = file( os.path.join(
- tmp_dir,
- str(time.time()) + '.foot.html'
- ),
- 'w'
- )
- foot_file.write(self._sanitize_html(footer.encode('utf-8')))
- foot_file.close()
+ with tempfile.NamedTemporaryFile(suffix=".foot.html",
+ delete=False) as foot_file:
+ foot_file.write(self._sanitize_html(footer.encode('utf-8')))
file_to_del.append(foot_file.name)
command.extend(['--footer-html', foot_file.name])
command.extend(['--page-size', str(webkit_header.format).replace(',', '.')])
count = 0
for html in html_list :
- html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
- count += 1
- html_file.write(self._sanitize_html(html.encode('utf-8')))
- html_file.close()
+ with tempfile.NamedTemporaryFile(suffix="%d.body.html" %count,
+ delete=False) as html_file:
+ count += 1
+ html_file.write(self._sanitize_html(html.encode('utf-8')))
file_to_del.append(html_file.name)
command.append(html_file.name)
command.append(out_filename)
if status :
raise except_osv(_('Webkit error' ),
_("The command 'wkhtmltopdf' failed with error code = %s. Message: %s") % (status, error_message))
- pdf_file = open(out_filename, 'rb')
- pdf = pdf_file.read()
- pdf_file.close()
+ with open(out_filename, 'rb') as pdf_file:
+ pdf = pdf_file.read()
+ os.close(fd)
finally:
if stderr_fd is not None:
os.close(stderr_fd)
this.$('.select_records').on('click', self.on_click);
},
on_click: function(ev) {
+ event.preventDefault();
var self = this;
var model = this.options.model || this.field_manager.get_field_value(this.options.model_field);
this.pop = new instance.web.form.SelectCreatePopup(this);
});
}
else {
- var domain = ["id", "in", element_ids];
+ var domain = [["id", "in", element_ids]];
var domain_done = $.Deferred().resolve(domain);
}
$.when(domain_done).then(function (domain) {
var domain = self.pop.dataset.domain.concat(domain || []);
- self.set_value(JSON.stringify(domain))
+ self.set_value(domain);
});
});
- event.preventDefault();
},
});
else
return $.when();
}).done(function () {
- if (!self.o2m.options.reload_on_button) {
+ var ds = self.o2m.dataset;
+ var cached_records = _.any([ds.to_create, ds.to_delete, ds.to_write], function(value) {
+ return value.length;
+ });
+ if (!self.o2m.options.reload_on_button && !cached_records) {
self.handle_button(name, id, callback);
}else {
self.handle_button(name, id, function(){
return response
def _handle_exception(self, exception=None, code=500):
- res = super(ir_http, self)._handle_exception(exception)
- if isinstance(exception, werkzeug.exceptions.HTTPException) and hasattr(exception, 'response') and exception.response:
- return exception.response
-
- attach = self._serve_attachment()
- if attach:
- return attach
-
- if getattr(request, 'website_enabled', False) and request.website:
- values = dict(
- exception=exception,
- traceback=traceback.format_exc(exception),
- )
- if exception:
- code = getattr(exception, 'code', code)
- if isinstance(exception, ir_qweb.QWebException):
- values.update(qweb_exception=exception)
- if isinstance(exception.qweb.get('cause'), openerp.exceptions.AccessError):
- code = 403
- if code == 500:
- logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
- if 'qweb_exception' in values:
- view = request.registry.get("ir.ui.view")
- views = view._views_get(request.cr, request.uid, exception.qweb['template'], request.context)
- to_reset = [v for v in views if v.model_data_id.noupdate is True]
- values['views'] = to_reset
- elif code == 403:
- logger.warn("403 Forbidden:\n\n%s", values['traceback'])
-
- values.update(
- status_message=werkzeug.http.HTTP_STATUS_CODES[code],
- status_code=code,
- )
-
- if not request.uid:
- self._auth_method_public()
-
- try:
- html = request.website._render('website.%s' % code, values)
- except Exception:
- html = request.website._render('website.http_error', values)
- return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
+ try:
+ return super(ir_http, self)._handle_exception(exception)
+ except Exception:
- return res
+ attach = self._serve_attachment()
+ if attach:
+ return attach
+
+ if getattr(request, 'website_enabled', False) and request.website:
+ values = dict(
+ exception=exception,
+ traceback=traceback.format_exc(exception),
+ )
+ if exception:
+ code = getattr(exception, 'code', code)
+ if isinstance(exception, ir_qweb.QWebException):
+ values.update(qweb_exception=exception)
+ if isinstance(exception.qweb.get('cause'), openerp.exceptions.AccessError):
+ code = 403
+ if code == 500:
+ logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
+ if 'qweb_exception' in values:
+ view = request.registry.get("ir.ui.view")
+ views = view._views_get(request.cr, request.uid, exception.qweb['template'], request.context)
+ to_reset = [v for v in views if v.model_data_id.noupdate is True]
+ values['views'] = to_reset
+ elif code == 403:
+ logger.warn("403 Forbidden:\n\n%s", values['traceback'])
+
+ values.update(
+ status_message=werkzeug.http.HTTP_STATUS_CODES[code],
+ status_code=code,
+ )
+
+ if not request.uid:
+ self._auth_method_public()
+
+ try:
+ html = request.website._render('website.%s' % code, values)
+ except Exception:
+ html = request.website._render('website.http_error', values)
+ return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
+
+ raise
class ModelConverter(ir.ir_http.ModelConverter):
def __init__(self, url_map, model=False, domain='[]'):
id: this.$target.data('id'),
fields: ['name', 'alias_id'],
}).always(function (data) {
- console.log(data);
self.is_user = data.is_user;
self.$target.find('.js_mg_email').attr('href', 'mailto:' + data.alias_id[1]);
self.$target.find('.js_mg_link').attr('href', '/groups/' + data.id);
class="js_follow_email form-control"
placeholder="your email..."/>
<span class="input-group-btn">
- <!-- <button href="#" class="btn btn-default js_unfollow_btn">Unsubscribe</button> -->
<button href="#" class="btn btn-primary js_follow_btn">Subscribe</button>
</span>
</div>
index = 0
maxy = 0
for p in products:
- x = p.website_size_x
- y = p.website_size_y
+ x = min(max(p.website_size_x, 1), PPR)
+ y = min(max(p.website_size_y, 1), PPR)
if index>PPG:
x = y = 1
_inherit = ["product.template", "website.seo.metadata"]
_order = 'website_published desc, website_sequence desc, name'
_name = 'product.template'
+ _mail_post_access = 'read'
def _website_url(self, cr, uid, ids, field_name, arg, context=None):
res = dict.fromkeys(ids, '')
to abitrary responses. Anything returned (except None) will
be used as response."""
self._failed = exception # prevent tx commit
+ if isinstance(exception, werkzeug.exceptions.HTTPException):
+ return exception
+ raise
def _call_function(self, *args, **kwargs):
request = self
def _handle_exception(self, exception):
"""Called within an except block to allow converting exceptions
to abitrary responses. Anything returned (except None) will
- be used as response."""
- super(JsonRequest, self)._handle_exception(exception)
- _logger.exception("Exception during JSON request handling.")
- error = {
- 'code': 200,
- 'message': "OpenERP Server Error",
- 'data': serialize_exception(exception)
- }
- if isinstance(exception, AuthenticationError):
- error['code'] = 100
- error['message'] = "OpenERP Session Invalid"
- return self._json_response(error=error)
+ be used as response."""
+ try:
+ return super(JsonRequest, self)._handle_exception(exception)
+ except Exception:
+ _logger.exception("Exception during JSON request handling.")
+ error = {
+ 'code': 200,
+ 'message': "OpenERP Server Error",
+ 'data': serialize_exception(exception)
+ }
+ if isinstance(exception, AuthenticationError):
+ error['code'] = 100
+ error['message'] = "OpenERP Session Invalid"
+ return self._json_response(error=error)
def dispatch(self):
""" Calls the method asked for by the JSON-RPC2 or JSONP request
if story_cnt > 0:
fis.append(platypus.PageBreak())
fis += r.render(node_story)
+ # Reset Page Number with new story tag
+ fis.append(PageReset())
story_cnt += 1
try:
if self.localcontext and self.localcontext.get('internal_header',False):