[MERGE] forward port branch saas-14 up to 48f88992da
This commit is contained in:
@@ -91,7 +91,7 @@ class Partner(models.Model):
|
|||||||
for partner in self:
|
for partner in self:
|
||||||
if not partner.street:
|
if not partner.street:
|
||||||
for field in street_fields:
|
for field in street_fields:
|
||||||
partner[field] = ''
|
partner[field] = None
|
||||||
continue
|
continue
|
||||||
|
|
||||||
street_format = (partner.country_id.street_format or
|
street_format = (partner.country_id.street_format or
|
||||||
|
|||||||
@@ -305,6 +305,8 @@ class Import(models.TransientModel):
|
|||||||
if all(val.lower() in ('true', 'false', 't', 'f', '') for val in preview_values):
|
if all(val.lower() in ('true', 'false', 't', 'f', '') for val in preview_values):
|
||||||
return ['boolean']
|
return ['boolean']
|
||||||
# If all values can be cast to float, type is either float or monetary
|
# If all values can be cast to float, type is either float or monetary
|
||||||
|
# Or a date/datetime if it matches the pattern
|
||||||
|
results = []
|
||||||
try:
|
try:
|
||||||
thousand_separator = decimal_separator = False
|
thousand_separator = decimal_separator = False
|
||||||
for val in preview_values:
|
for val in preview_values:
|
||||||
@@ -336,7 +338,7 @@ class Import(models.TransientModel):
|
|||||||
if thousand_separator and not options.get('float_decimal_separator'):
|
if thousand_separator and not options.get('float_decimal_separator'):
|
||||||
options['float_thousand_separator'] = thousand_separator
|
options['float_thousand_separator'] = thousand_separator
|
||||||
options['float_decimal_separator'] = decimal_separator
|
options['float_decimal_separator'] = decimal_separator
|
||||||
return ['float', 'monetary']
|
results = ['float', 'monetary']
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
# Try to see if all values are a date or datetime
|
# Try to see if all values are a date or datetime
|
||||||
@@ -372,13 +374,15 @@ class Import(models.TransientModel):
|
|||||||
current_date_pattern = check_patterns(date_patterns, preview_values)
|
current_date_pattern = check_patterns(date_patterns, preview_values)
|
||||||
if current_date_pattern:
|
if current_date_pattern:
|
||||||
options['date_format'] = current_date_pattern
|
options['date_format'] = current_date_pattern
|
||||||
return ['date']
|
results += ['date']
|
||||||
|
|
||||||
current_datetime_pattern = check_patterns(datetime_patterns, preview_values)
|
current_datetime_pattern = check_patterns(datetime_patterns, preview_values)
|
||||||
if current_datetime_pattern:
|
if current_datetime_pattern:
|
||||||
options['datetime_format'] = current_datetime_pattern
|
options['datetime_format'] = current_datetime_pattern
|
||||||
return ['datetime']
|
results += ['datetime']
|
||||||
|
|
||||||
|
if results:
|
||||||
|
return results
|
||||||
return ['text', 'char', 'datetime', 'selection', 'many2one', 'one2many', 'many2many', 'html']
|
return ['text', 'char', 'datetime', 'selection', 'many2one', 'one2many', 'many2many', 'html']
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
@@ -500,7 +504,7 @@ class Import(models.TransientModel):
|
|||||||
'headers_type': header_types or False,
|
'headers_type': header_types or False,
|
||||||
'preview': preview,
|
'preview': preview,
|
||||||
'options': options,
|
'options': options,
|
||||||
'advanced_mode': any([len(models.fix_import_export_id_paths(col)) > 1 for col in headers]),
|
'advanced_mode': any([len(models.fix_import_export_id_paths(col)) > 1 for col in headers or []]),
|
||||||
'debug': self.user_has_groups('base.group_no_one'),
|
'debug': self.user_has_groups('base.group_no_one'),
|
||||||
}
|
}
|
||||||
except Exception, error:
|
except Exception, error:
|
||||||
|
|||||||
@@ -201,9 +201,9 @@ class DeliveryCarrier(models.Model):
|
|||||||
return False
|
return False
|
||||||
if self.state_ids and contact.state_id not in self.state_ids:
|
if self.state_ids and contact.state_id not in self.state_ids:
|
||||||
return False
|
return False
|
||||||
if self.zip_from and (contact.zip or '') < self.zip_from:
|
if self.zip_from and (contact.zip or '').upper() < self.zip_from.upper():
|
||||||
return False
|
return False
|
||||||
if self.zip_to and (contact.zip or '') > self.zip_to:
|
if self.zip_to and (contact.zip or '').upper() > self.zip_to.upper():
|
||||||
return False
|
return False
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class LivechatController(http.Controller):
|
|||||||
# if the user is identifiy (eg: portal user on the frontend), don't use the anonymous name. The user will be added to session.
|
# if the user is identifiy (eg: portal user on the frontend), don't use the anonymous name. The user will be added to session.
|
||||||
if request.session.uid:
|
if request.session.uid:
|
||||||
anonymous_name = request.env.user.name
|
anonymous_name = request.env.user.name
|
||||||
return request.env["im_livechat.channel"].get_mail_channel(channel_id, anonymous_name)
|
return request.env["im_livechat.channel"].with_context(lang=False).get_mail_channel(channel_id, anonymous_name)
|
||||||
|
|
||||||
@http.route('/im_livechat/feedback', type='json', auth='public')
|
@http.route('/im_livechat/feedback', type='json', auth='public')
|
||||||
def feedback(self, uuid, rate, reason=None, **kwargs):
|
def feedback(self, uuid, rate, reason=None, **kwargs):
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ class MrpUnbuild(models.Model):
|
|||||||
self.env['stock.quant'].quants_reserve(quants, consume_move)
|
self.env['stock.quant'].quants_reserve(quants, consume_move)
|
||||||
|
|
||||||
if consume_move.has_tracking != 'none':
|
if consume_move.has_tracking != 'none':
|
||||||
|
if not quants[0][0]:
|
||||||
|
raise UserError(_("You don't have in the stock the lot %s.") % (self.lot_id.name,))
|
||||||
self.env['stock.move.lots'].create({
|
self.env['stock.move.lots'].create({
|
||||||
'move_id': consume_move.id,
|
'move_id': consume_move.id,
|
||||||
'lot_id': self.lot_id.id,
|
'lot_id': self.lot_id.id,
|
||||||
@@ -119,6 +121,8 @@ class MrpUnbuild(models.Model):
|
|||||||
for produce_move in produce_moves:
|
for produce_move in produce_moves:
|
||||||
if produce_move.has_tracking != 'none':
|
if produce_move.has_tracking != 'none':
|
||||||
original = original_quants.filtered(lambda quant: quant.product_id == produce_move.product_id)
|
original = original_quants.filtered(lambda quant: quant.product_id == produce_move.product_id)
|
||||||
|
if not original.lot_id:
|
||||||
|
raise UserError(_("You don't have in the stock the required lot/serial number for %s .") % (produce_move.product_id.name,))
|
||||||
self.env['stock.move.lots'].create({
|
self.env['stock.move.lots'].create({
|
||||||
'move_id': produce_move.id,
|
'move_id': produce_move.id,
|
||||||
'lot_id': original.lot_id.id,
|
'lot_id': original.lot_id.id,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class Repair(models.Model):
|
|||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _default_stock_location(self):
|
def _default_stock_location(self):
|
||||||
warehouse = self.env.ref('stock.warehouse0', raise_if_not_found=False)
|
warehouse = self.env['stock.warehouse'].search([], limit=1)
|
||||||
if warehouse:
|
if warehouse:
|
||||||
return warehouse.lot_stock_id.id
|
return warehouse.lot_stock_id.id
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -143,15 +143,15 @@ class Product(models.Model):
|
|||||||
Quant = self.env['stock.quant']
|
Quant = self.env['stock.quant']
|
||||||
domain_move_in_todo = [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_move_in
|
domain_move_in_todo = [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_move_in
|
||||||
domain_move_out_todo = [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_move_out
|
domain_move_out_todo = [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_move_out
|
||||||
moves_in_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_in_todo, ['product_id', 'product_qty'], ['product_id']))
|
moves_in_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_in_todo, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
||||||
moves_out_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_out_todo, ['product_id', 'product_qty'], ['product_id']))
|
moves_out_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_out_todo, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
||||||
quants_res = dict((item['product_id'][0], item['qty']) for item in Quant.read_group(domain_quant, ['product_id', 'qty'], ['product_id']))
|
quants_res = dict((item['product_id'][0], item['qty']) for item in Quant.read_group(domain_quant, ['product_id', 'qty'], ['product_id'], orderby='id'))
|
||||||
if dates_in_the_past:
|
if dates_in_the_past:
|
||||||
# Calculate the moves that were done before now to calculate back in time (as most questions will be recent ones)
|
# Calculate the moves that were done before now to calculate back in time (as most questions will be recent ones)
|
||||||
domain_move_in_done = [('state', '=', 'done'), ('date', '>', to_date)] + domain_move_in_done
|
domain_move_in_done = [('state', '=', 'done'), ('date', '>', to_date)] + domain_move_in_done
|
||||||
domain_move_out_done = [('state', '=', 'done'), ('date', '>', to_date)] + domain_move_out_done
|
domain_move_out_done = [('state', '=', 'done'), ('date', '>', to_date)] + domain_move_out_done
|
||||||
moves_in_res_past = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_in_done, ['product_id', 'product_qty'], ['product_id']))
|
moves_in_res_past = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_in_done, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
||||||
moves_out_res_past = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_out_done, ['product_id', 'product_qty'], ['product_id']))
|
moves_out_res_past = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_out_done, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
|
||||||
|
|
||||||
res = dict()
|
res = dict()
|
||||||
for product in self.with_context(prefetch_fields=False):
|
for product in self.with_context(prefetch_fields=False):
|
||||||
@@ -288,7 +288,7 @@ class Product(models.Model):
|
|||||||
domain_quant.append(('owner_id', '=', owner_id))
|
domain_quant.append(('owner_id', '=', owner_id))
|
||||||
if package_id:
|
if package_id:
|
||||||
domain_quant.append(('package_id', '=', package_id))
|
domain_quant.append(('package_id', '=', package_id))
|
||||||
quants_groupby = self.env['stock.quant'].read_group(domain_quant, ['product_id', 'qty'], ['product_id'])
|
quants_groupby = self.env['stock.quant'].read_group(domain_quant, ['product_id', 'qty'], ['product_id'], orderby='id')
|
||||||
for quant in quants_groupby:
|
for quant in quants_groupby:
|
||||||
if OPERATORS[operator](quant['qty'], value):
|
if OPERATORS[operator](quant['qty'], value):
|
||||||
product_ids.add(quant['product_id'][0])
|
product_ids.add(quant['product_id'][0])
|
||||||
|
|||||||
@@ -174,8 +174,8 @@ var CompletionFieldMixin = {
|
|||||||
init: function() {
|
init: function() {
|
||||||
this.limit = 7;
|
this.limit = 7;
|
||||||
this.orderer = new utils.DropMisordered();
|
this.orderer = new utils.DropMisordered();
|
||||||
this.can_create = this.node.attrs.can_create || true;
|
this.can_create = this.node.attrs.can_create == "false" ? false : true;
|
||||||
this.can_write = this.node.attrs.can_write || true;
|
this.can_write = this.node.attrs.can_write == "false" ? false : true;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Call this method to search using a string.
|
* Call this method to search using a string.
|
||||||
|
|||||||
@@ -1091,7 +1091,8 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
|||||||
|| field.field.type === 'one2many'
|
|| field.field.type === 'one2many'
|
||||||
|| field.field.type === 'many2many'
|
|| field.field.type === 'many2many'
|
||||||
|| field.field.type === 'binary'
|
|| field.field.type === 'binary'
|
||||||
|| field.password) {
|
|| field.password
|
||||||
|
|| !_.isEmpty(field.field.depends)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -504,7 +504,7 @@ dom.removeBetween = function (sc, so, ec, eo, towrite) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (var i=0; i<nodes.length; i++) {
|
for (var i=0; i<nodes.length; i++) {
|
||||||
if (!dom.ancestor(nodes[i], ancestor_first_last) && !$.contains(nodes[i], before) && !$.contains(nodes[i], after)) {
|
if (!dom.ancestor(nodes[i], ancestor_first_last) && !$.contains(nodes[i], before) && !$.contains(nodes[i], after) && !dom.isEditable(nodes[i])) {
|
||||||
nodes[i].parentNode.removeChild(nodes[i]);
|
nodes[i].parentNode.removeChild(nodes[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1121,6 +1121,9 @@ $.summernote.pluginEvents.enter = function (event, editor, layoutInfo) {
|
|||||||
// double enter on the end of a blockquote & pre = new line out of the list
|
// double enter on the end of a blockquote & pre = new line out of the list
|
||||||
$('<p></p>').append(br).insertAfter($(r.sc).closest('blockquote, pre'));
|
$('<p></p>').append(br).insertAfter($(r.sc).closest('blockquote, pre'));
|
||||||
node = br;
|
node = br;
|
||||||
|
} else if (dom.isEditable(dom.node(r.sc))) {
|
||||||
|
// if we are directly in an editable, only SHIFT + ENTER should add a newline
|
||||||
|
node = null;
|
||||||
} else if (last === r.sc) {
|
} else if (last === r.sc) {
|
||||||
if (dom.isBR(last)) {
|
if (dom.isBR(last)) {
|
||||||
last = last.parentNode;
|
last = last.parentNode;
|
||||||
|
|||||||
@@ -914,7 +914,7 @@ var VideoDialog = Widget.extend({
|
|||||||
video_id = this.$("#video_id").val();
|
video_id = this.$("#video_id").val();
|
||||||
}
|
}
|
||||||
var $iframe = $(
|
var $iframe = $(
|
||||||
'<div class="media_iframe_video" data-src="'+this.$iframe.attr("src")+'">'+
|
'<div class="media_iframe_video" data-oe-expression="'+this.$iframe.attr("src")+'">'+
|
||||||
'<div class="css_editable_mode_display"> </div>'+
|
'<div class="css_editable_mode_display"> </div>'+
|
||||||
'<div class="media_iframe_video_size" contentEditable="false"> </div>'+
|
'<div class="media_iframe_video_size" contentEditable="false"> </div>'+
|
||||||
'<iframe src="'+this.$iframe.attr("src")+'" frameborder="0" allowfullscreen="allowfullscreen" contentEditable="false"></iframe>'+
|
'<iframe src="'+this.$iframe.attr("src")+'" frameborder="0" allowfullscreen="allowfullscreen" contentEditable="false"></iframe>'+
|
||||||
|
|||||||
@@ -418,7 +418,8 @@ animation.registry.media_video = animation.Class.extend({
|
|||||||
var size = '<div class="media_iframe_video_size"> </div>';
|
var size = '<div class="media_iframe_video_size"> </div>';
|
||||||
this.$target.html(editor+size);
|
this.$target.html(editor+size);
|
||||||
}
|
}
|
||||||
this.$target.html(this.$target.html()+'<iframe src="'+_.escape(this.$target.data("src"))+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>');
|
// rebuilding the iframe, from https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
|
||||||
|
this.$target.html(this.$target.html()+'<iframe sandbox="allow-scripts allow-same-origin" src="'+_.escape(this.$target.data("oe-expression"))+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>');
|
||||||
return this._super.apply(this, arguments);
|
return this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ if(!$('.website_forum').length) {
|
|||||||
return $.Deferred().reject("DOM doesn't contain '.website_forum'");
|
return $.Deferred().reject("DOM doesn't contain '.website_forum'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pull-left class messes up the post layout OPW 769721
|
||||||
|
$('span[data-oe-model="forum.post"][data-oe-field="content"]').find('img.pull-left').removeClass('pull-left');
|
||||||
|
|
||||||
$("[data-toggle='popover']").popover();
|
$("[data-toggle='popover']").popover();
|
||||||
$('.karma_required').on('click', function (ev) {
|
$('.karma_required').on('click', function (ev) {
|
||||||
var karma = $(ev.currentTarget).data('karma');
|
var karma = $(ev.currentTarget).data('karma');
|
||||||
@@ -413,6 +416,9 @@ if(!$('.website_forum').length) {
|
|||||||
toolbar: toolbar,
|
toolbar: toolbar,
|
||||||
styleWithSpan: false
|
styleWithSpan: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// pull-left class messes up the post layout OPW 769721
|
||||||
|
$form.find('.note-editable').find('img.pull-left').removeClass('pull-left');
|
||||||
$form.on('click', 'button, .a-submit', function () {
|
$form.on('click', 'button, .a-submit', function () {
|
||||||
$textarea.html($form.find('.note-editable').code());
|
$textarea.html($form.find('.note-editable').code());
|
||||||
});
|
});
|
||||||
|
|||||||
+8
-2
@@ -1504,8 +1504,14 @@ def db_filter(dbs, httprequest=None):
|
|||||||
d, _, r = h.partition('.')
|
d, _, r = h.partition('.')
|
||||||
if d == "www" and r:
|
if d == "www" and r:
|
||||||
d = r.partition('.')[0]
|
d = r.partition('.')[0]
|
||||||
r = odoo.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
|
if odoo.tools.config['dbfilter']:
|
||||||
dbs = [i for i in dbs if re.match(r, i)]
|
r = odoo.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
|
||||||
|
dbs = [i for i in dbs if re.match(r, i)]
|
||||||
|
elif odoo.tools.config['db_name']:
|
||||||
|
# In case --db-filter is not provided and --database is passed, Odoo will
|
||||||
|
# use the value of --database as a comma seperated list of exposed databases.
|
||||||
|
exposed_dbs = set(db.strip() for db in odoo.tools.config['db_name'].split(','))
|
||||||
|
dbs = sorted(exposed_dbs.intersection(dbs))
|
||||||
return dbs
|
return dbs
|
||||||
|
|
||||||
def db_monodb(httprequest=None):
|
def db_monodb(httprequest=None):
|
||||||
|
|||||||
@@ -321,6 +321,14 @@ def exp_db_exist(db_name):
|
|||||||
def list_dbs(force=False):
|
def list_dbs(force=False):
|
||||||
if not odoo.tools.config['list_db'] and not force:
|
if not odoo.tools.config['list_db'] and not force:
|
||||||
raise odoo.exceptions.AccessDenied()
|
raise odoo.exceptions.AccessDenied()
|
||||||
|
|
||||||
|
if not odoo.tools.config['dbfilter'] and odoo.tools.config['db_name']:
|
||||||
|
# In case --db-filter is not provided and --database is passed, Odoo will not
|
||||||
|
# fetch the list of databases available on the postgres server and instead will
|
||||||
|
# use the value of --database as comma seperated list of exposed databases.
|
||||||
|
res = sorted(db.strip() for db in odoo.tools.config['db_name'].split(','))
|
||||||
|
return res
|
||||||
|
|
||||||
chosen_template = odoo.tools.config['db_template']
|
chosen_template = odoo.tools.config['db_template']
|
||||||
templates_list = tuple(set(['postgres', chosen_template]))
|
templates_list = tuple(set(['postgres', chosen_template]))
|
||||||
db = odoo.sql_db.db_connect('postgres')
|
db = odoo.sql_db.db_connect('postgres')
|
||||||
|
|||||||
@@ -22,9 +22,13 @@ def _convert_nn_fr(val):
|
|||||||
if val < 20:
|
if val < 20:
|
||||||
return to_19_fr[val]
|
return to_19_fr[val]
|
||||||
for (dcap, dval) in ((k, 20 + (10 * v)) for (v, k) in enumerate(tens_fr)):
|
for (dcap, dval) in ((k, 20 + (10 * v)) for (v, k) in enumerate(tens_fr)):
|
||||||
if dval + 10 > val:
|
base = 10
|
||||||
if val % 10:
|
if dval in [60, 80]:
|
||||||
return dcap + '-' + to_19_fr[val % 10]
|
base = 20
|
||||||
|
if dval + base > val:
|
||||||
|
if val % base:
|
||||||
|
bond = (val % base in [1, 11] and dval != 80) and '-et-' or '-'
|
||||||
|
return dcap + bond + to_19_fr[val % base]
|
||||||
return dcap
|
return dcap
|
||||||
|
|
||||||
def _convert_nnn_fr(val):
|
def _convert_nnn_fr(val):
|
||||||
@@ -37,7 +41,7 @@ def _convert_nnn_fr(val):
|
|||||||
word = ''
|
word = ''
|
||||||
(mod, rem) = (val % 100, val // 100)
|
(mod, rem) = (val % 100, val // 100)
|
||||||
if rem > 0:
|
if rem > 0:
|
||||||
word = to_19_fr[rem] + ' Cent'
|
word = (rem > 1 and to_19_fr[rem] + ' cents') or 'Cent'
|
||||||
if mod > 0:
|
if mod > 0:
|
||||||
word += ' '
|
word += ' '
|
||||||
if mod > 0:
|
if mod > 0:
|
||||||
@@ -92,7 +96,7 @@ def _convert_nn_nl(val):
|
|||||||
for (dcap, dval) in ((k, 20 + (10 * v)) for (v, k) in enumerate(tens_nl)):
|
for (dcap, dval) in ((k, 20 + (10 * v)) for (v, k) in enumerate(tens_nl)):
|
||||||
if dval + 10 > val:
|
if dval + 10 > val:
|
||||||
if val % 10:
|
if val % 10:
|
||||||
return dcap + '-' + to_19_nl[val % 10]
|
return to_19_nl[val % 10] + '-en-' + dcap
|
||||||
return dcap
|
return dcap
|
||||||
|
|
||||||
def _convert_nnn_nl(val):
|
def _convert_nnn_nl(val):
|
||||||
@@ -105,7 +109,7 @@ def _convert_nnn_nl(val):
|
|||||||
word = ''
|
word = ''
|
||||||
(mod, rem) = (val % 100, val // 100)
|
(mod, rem) = (val % 100, val // 100)
|
||||||
if rem > 0:
|
if rem > 0:
|
||||||
word = to_19_nl[rem] + ' Honderd'
|
word = (rem > 1 and to_19_nl[rem] + 'honderd') or 'Honderd'
|
||||||
if mod > 0:
|
if mod > 0:
|
||||||
word += ' '
|
word += ' '
|
||||||
if mod > 0:
|
if mod > 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user