how to implement a section block in eLearning module - odoo 14 - odoo

i was tryin to add a section block which allows me to block all the possible sections (except for the first) and then if all the slides in the section are complete, makes the next one available
I tried with the templates but it don't seem the way to approach this problem cause I cannot find any way to check the previous slides, I was trying to figure it out with javascript maybe but I'm not that good with JS
that's my actual progress:
<odoo>
<data>
<template id="section_lock" inherit_id='website_slides.course_slides_list_slide'>
<xpath expr="//div[#class='text-truncate mr-auto']" position="replace">
<div class="text-truncate mr-auto">
<a t-if="slide.is_preview or slide.previous_category_complete is False or channel.can_publish"
class="o_wslides_js_slides_list_slide_link" t-attf-href="/slides/slide/#{slug(slide)}">
<span t-field="slide.name"/>
</a>
<span t-else="">
<span t-esc="slide.name"/>
</span>
</div>
</xpath>
</template>
</data>
and then the model with the flag needed for the check
class Slide(models.Model):
_inherit = 'slide.slide'
previous_category_complete = fields.Boolean(default=True)
how do u suggest to do it? I think i'm way off th road with this

The relationship between an e-Course and its section is based on the same model (slide.slide) using these relational fields :
category_id = fields.Many2one('slide.slide', string="Section", compute="_compute_category_id", store=True)
slide_ids = fields.One2many('slide.slide', "category_id", string="Slides")
#api.depends('channel_id.slide_ids.is_category', 'channel_id.slide_ids.sequence')
def _compute_category_id(self):
self.category_id = False # initialize whatever the state
channel_slides = {}
for slide in self:
if slide.channel_id.id not in channel_slides:
channel_slides[slide.channel_id.id] = slide.channel_id.slide_ids
for cid, slides in channel_slides.items():
current_category = self.env['slide.slide']
slide_list = list(slides)
slide_list.sort(key=lambda s: (s.sequence, not s.is_category))
for slide in slide_list:
if slide.is_category:
current_category = slide
elif slide.category_id != current_category:
slide.category_id = current_category.id
To know wether a specific student has attended an e-course, there is an existing field in the model 'slide.slide.partner':
class SlidePartnerRelation(models.Model):
_name = 'slide.slide.partner'
slide_id = fields.Many2one('slide.slide', ondelete="cascade", index=True, required=True)
partner_id = fields.Many2one('res.partner', index=True, required=True, ondelete='cascade')
completed = fields.Boolean('Completed')
So you can check wether all the previous Slide Sections are completed for the current partner_id, inheriting the class WebsiteSlides located in website_slides/controllers/main.py > def channel(...)... :
from odoo.addons.website_slides.controllers.main import WebsiteSlides
class WebsiteSlidesCustom(WebsiteSlides):
#http.route()
def channel(self, channel, category=None, tag=None, page=1, slide_type=None, uncategorized=False, sorting=None, search=None, **kw):
res_super = super(WebsiteSlidesCustom, self).channel(channel, category=None, tag=None, page=1, slide_type=None, uncategorized=False, sorting=None, search=None, **kw)
values = res_super.qcontext
sections_completion = request.env['slide.slide.partner'].sudo().search([
('channel_id', '=', current_channel_id),
('is_category','=', True),
('partner_id', '=', request.env.user.partner_id.id)
])
dic_sections_completion={}
for sec in sections_completion.sorted('sequence'):
dic_sections_completion[sec.id] = sec.completed
values['sections_completion'] = dic_sections_completion
return request.render('website_slides.course_main', values)
in your module directory views, create a new xml file : course_main_custom.xml
<template id="course_main_custom" inherit_id="website_slides.course_main" name="Course Main" >
<xpath expr="//t[#t-set='category']" position="after">
...
</xpath>
</template>

Related

how to print looped data from a method

i created a method as below: but it only takes the last loop data not all the data to xml for generating pdf,so need a way to generate pdf on a loop from this method
def bsku_asin_picking(self):
website = self.env['website'].search([('company_id', '=', self.env.company.id)], limit=1)
# print('%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%', website.label_type)
# vals = []
for rec in self.move_ids_without_package:
product = self.env['product.product'].browse(rec.product_id.id)
data = {
'quantity': rec.product_uom_qty,
'display_name': product.display_name,
'product': product,
}
if website.label_type == 'bsku':
data['barcode_value'] = product.bsku
else:
data['barcode_value'] = product.asin
# vals.append(data)
return self.env.ref('product_label_bsku_sin.report_product_template_label').report_action(product, data=data)
The variables passed through data will be available in the rendering context.
Example:
def bsku_asin_picking(self):
website = self.env['website'].search([('company_id', '=', self.env.company.id)], limit=1)
vals = []
for rec in self.move_ids_without_package:
product = rec.product_id
product_data = {
'quantity': rec.product_uom_qty,
'display_name': product.display_name,
'product': product,
}
if website.label_type == 'bsku':
product_data['barcode_value'] = product.bsku
else:
product_data['barcode_value'] = product.asin
vals.append(product_data)
datas = {'vals': vals}
return self.env.ref('product_label_bsku_sin.action_report_product_template_label').report_action(self, data=datas)
Then you can loop over vals and print product data.
Example:
<t t-foreach="vals" t-as="product_data">
<p t-out="product_data['quantity']"/>
<p t-out="product_data['display_name']"/>
<p t-out="product_data['product']"/>
<p t-out="product_data['barcode_value']"/>
</t>
Alternative
You can also call report_action without data and loop over the move_ids_without_package:
Example:
<t t-set="website_label_type" t-value="doc.env['website'].search([('company_id', '=', doc.env.company.id)], limit=1)"/>
<t t-foreach="doc.move_ids_without_package" t-as="line">
<p t-out="line.product_uom_qty"/>
<p t-out="line.display_name"/>
<p t-out="line.product_id.name"/>
<t t-if="website_label_type == 'bsku'">
<span t-out="line.product_id.bsku"/>
</t>
<t t-else="">
<span t-out="line.product_id.asin"/>
</t>
</t>

Odoo 12 num2words Amount To Text

I'm new in this community and I setting up Odoo Community version for my little comapny. I does all the things just doesn't know how to set up num2words to show Total Amount in Invoice Reports!
I found num2words api in res_currency.py in Base/Modules but I spend two days researching how to connect and nothing. What I have to inherite and how and also what to put in invoice document qweb?
I made module like this:
from num2words import num2words
class account_invoice(models.Model):
_inherit = "account.invoice"
#api.multi
def amount_to_text(self, amount):
self.ensure_one()
def _num2words(number, lang):
try:
return num2words(number, lang=lang).title()
except NotImplementedError:
return num2words(number, lang='en').title()
if num2words is None:
logging.getLogger(__name__).warning("The library 'num2words' is missing, cannot render textual amounts.")
return ""
formatted = "%.{0}f".format(self.decimal_places) % amount
parts = formatted.partition('.')
integer_value = int(parts[0])
fractional_value = int(parts[2] or 0)
lang_code = self.env.context.get('lang') or self.env.user.lang
lang = self.env['res.lang'].search([('code', '=', lang_code)])
amount_words = tools.ustr('{amt_value} {amt_word}').format(
amt_value=_num2words(integer_value, lang=lang.iso_code),
amt_word=self.currency_unit_label,
)
if not self.is_zero(amount - integer_value):
amount_words += ' ' + _('and') + tools.ustr(' {amt_value} {amt_word}').format(
amt_value=_num2words(fractional_value, lang=lang.iso_code),
amt_word=self.currency_subunit_label,
)
return amount_words
Got error like this:
Error to render compiling AST
AttributeError: 'NoneType' object has no attribute 'currency_id'
Template: account.report_invoice_document_with_payments
Path: /templates/t/t/div/p[1]/span
Node: <span t-if="doc.currency_id" t-esc="doc.currency_id.amount_to_text(doc.amount_total)"/>
In QWeb I put this:
<span t-if="doc.currency_id" t-esc="doc.currency_id.amount_to_text(doc.amount_total)"/>
Thank you in advance!
In your case "currency_id" is a Many2one field. The 'res.currency' model does not contain the class 'amount_to_text' function.
You have written amount_to_text function in 'account.invoice' model. So change your like this,
<span t-if="doc.currency_id" t-esc="doc.amount_to_text(doc.amount_total)"/>
OR(if you don't have currency_id field in your object)
<span t-if="doc.amount_to_text" t-esc="doc.amount_to_text(doc.amount_total)"/>
Please use the below code
<span t-if="o.currency_id" t-esc="o.amount_to_text(o.amount_total)"/>
You are getting the error because in the base report they are using o instead of doc. please see the below part of code from base.
<t t-foreach="docs" t-as="o">
So try to use o instead of doc

How to display label of a field in qweb reports?

Is there a way to display the label we put on a field in a qweb reports?
For example
In my .py
findings = fields.Text(string="Findings")
And in my .xml
<t t-esc="findings" /> <!-- only shows the value -->
Can we also get the label in qweb?
You can not get the label of the field.
Instead you can add html tag to show the label
Ex:
<p>Your Label <t t-esc="findings" /> </p>
or
<span> Some Text <t t-esc="findings" /> </span>
You can get field description (label) using a function but I encourage you to display labels as they did in odoo invoice reports.
To get date_invoice label:
def get_field_label(self, model_name, field_name):
ir_model_obj = self.env['ir.model']
ir_model_fields_obj = self.env['ir.model.fields']
model_id = ir_model_obj.search([('model', '=', model_name)], limit=1)
field_id = ir_model_fields_obj.search([('name', '=', field_name), ('model_id', '=', model_id.id)], limit=1)
return field_id.field_description

Odoo11 Custom Report with Wizard

I would like to print some product barcodes with the custom configuration.
Now, the problem is, I have created the wizard but when I click the print button there is no data is transferred to the report.
class BarcodeConfig(models.Model):
_name = 'report.barcode.print'
#api.model
def _default_product_line(self):
active_ids = self._context.get('active_ids', [])
products = self.env['product.product'].browse(active_ids)
return [(0, 0, {'product_id': x.id, 'qty': 1}) for x in products]
product_line = fields.One2many('product.print.wizard', 'barcode_id', string="Products",
default=_default_product_line)
#api.multi
def print_report(self):
data = self.read()[0]
ids = [x.product_id.id for x in self.product_line]
config = self.env['barcode.config'].get_values()
data.update({
'config': config
})
datas = {'ids': ids,
'form': data
}
return self.env.ref('odoo_barcode.barcode_report').report_action(self,data=datas)
In the above code, I had ids as well for product.product model.
<template id="report_barcode_temp">
<t t-call="web.html_container">
<t t-call="web.internal_layout">
<div class="page">
<h2>Report title</h2>
<strong t-esc="docs"/>
<strong t-esc="doc_ids"/>
<span t-esc="data"/>
<t t-foreach="docs" t-as="o">
<span t-esc="o"/>
<p>This object's name is
<span t-field="o.name"/>
</p>
</t>
</div>
</t>
</t>
In the above code, I'm just trying to print what the data's I've, but I've nothing in docs.
<strong t-esc="docs"/>
this line print product.product model as well. but ids not here, That's the problem I have.
Can anyone help me to tell why this happens? Or is there any other way to achieve this?
I have solved this by get_report_values method.
class classname(models.AbstractModel):
_name = 'report.modulename.templatename'
#api.model
def get_report_values(self, docids, data=None):
# docids: pass from the wizard print button.
records = self.env[objectname].browse(docids)
return {
'doc_ids': docids,
'doc_model': objectname,
'docs': records,
'data': data,}

Odoo : Make changes on Survey module

I am working on editing the survey module in odoo.
When I answer a survey which is composed of pages and then click on submit survey, it calculates the whole score of the survey and displays it. What I want is that it calculates the score of each page and display it when I click on submit survey for example:
It displays: Your score is: 200 points.
And I want it to display: Score of first page : 20 points, Second page: 50 points and so on...
Here is the code that I want to change but I don't know how should I change this function.
questionnaire.py (survey.py) : class survey_user_input
class survey_user_input(osv.Model):
''' Metadata for a set of one user's answers to a particular survey '''
_name = "questionnaire.user_input"
_rec_name = 'date_create'
_description = 'Survey User Input'
def _quizz_get_score(self, cr, uid, ids, name, args, context=None):
ret = dict()
for user_input in self.browse(cr, uid, ids, context=context):
ret[user_input.id] = sum([uil.quizz_mark for uil in user_input.user_input_line_ids] or [0.0])
return ret
_columns = {
'survey_id': fields.many2one('questionnaire.questionnaire', 'Questionnaire', required=True,
readonly=1, ondelete='restrict'),
'date_create': fields.datetime('Date de creation', required=True,
readonly=1, copy=False),
'deadline': fields.datetime("Date limite",
oldname="date_deadline"),
'type': fields.selection([('manually', 'Manuellement'), ('link', 'Lien')],
'Type de reponse', required=1, readonly=1,
oldname="response_type"),
'state': fields.selection([('new', 'Pas encore commence'),
('skip', 'Partiellement acheve'),
('done', 'Termine')],
'Statut',
readonly=True),
'test_entry': fields.boolean('Entree de test', readonly=1),
'token': fields.char("Piece d'identite", readonly=1, required=1, copy=False),
# Optional Identification data
'partner_id': fields.many2one('res.partner', 'Partenaire', readonly=1),
'email': fields.char("E-mail", readonly=1),
# Displaying data
'last_displayed_page_id': fields.many2one('questionnaire.page',
'Derniere page affichee'),
# The answers !
'user_input_line_ids': fields.one2many('questionnaire.user_input_line',
'user_input_id', 'Reponses', copy=True),
# URLs used to display the answers
'result_url': fields.related('survey_id', 'result_url', type='char',
string="Lien public aux resultats du sondage"),
'print_url': fields.related('survey_id', 'print_url', type='char',
string="Lien public au sondage vide"),
'quizz_score': fields.function(_quizz_get_score, type="float", string="Score pour le quiz", store=True)
}
_defaults = {
'date_create': fields.datetime.now,
'type': 'manually',
'state': 'new',
'token': lambda s, cr, uid, c: uuid.uuid4().__str__(),
'quizz_score': 0.0,
}
questionnaire.py (survey.py) : class survey_user_input_line
_name = 'questionnaire.user_input_line'
_description = 'Survey User Input Line'
_rec_name = 'date_create'
_columns = {
'user_input_id': fields.many2one('questionnaire.user_input', 'Entree de l\'utilisateur',
ondelete='cascade', required=1),
'question_id': fields.many2one('questionnaire.question', 'Question',
ondelete='restrict', required=1),
'page_id': fields.related('question_id', 'page_id', type='many2one',
relation='questionnaire.page', string="Page"),
'survey_id': fields.related('user_input_id', 'survey_id',
type="many2one", relation="questionnaire.questionnaire",
string='Questionnaire', store=True),
'date_create': fields.datetime('Date de creation', required=1),
'skipped': fields.boolean('Ignore'),
'answer_type': fields.selection([('text', 'Texte'),
('number', 'Nombre'),
('date', 'Date'),
('free_text', 'Texte Libre'),
('suggestion', 'Suggestion')],
'Type de reponse'),
'value_text': fields.char("Reponse texte"),
'value_number': fields.float("Reponse numerique"),
'value_date': fields.datetime("Reponse date"),
'value_free_text': fields.text("Reponse texte libre"),
'value_suggested': fields.many2one('questionnaire.label', "Reponse suggeree"),
'value_suggested_row': fields.many2one('questionnaire.label', "Reponse en ligne"),
'quizz_mark': fields.float("Score donne pour ce choix")
}
survey_templates.xml
<!-- "Thank you" message when the survey is completed -->
<template id="sfinished" name="Survey Finished">
<t t-call="website.layout">
<div class="wrap">
<div class="container">
<t t-call="questionnaire.back" />
<div class="jumbotron mt32">
<h1>Thank you!</h1>
<div t-field="questionnaire.thank_you_message" class="oe_no_empty" />
<div> You scored <t t-esc="user_input.quizz_score" /> points.</div>
<div>If you want you can <a t-att-href="'/questionnaire/print/%s/%s' % (slug(questionnaire), token)">review your answers</a>.</div>
</div>
</div>
</div>
</t>
</template>
One alternative way is to use pure qweb rather than function field; like this:
<!-- First finding which page is related to the survey. This is a bit odd but I couldn't find any other way!! -->
<t t-set="pages" t-value="dict((l.id, l.page_id.id) for l in user_input.user_input_line_ids).values()" />
<!-- Then print the score per each page -->
<t t-foreach='pages' t-as='p'>
<t t-set="page_score" t-value="sum([(uil.quizz_mark or 0.0) if uil.page_id.id == p else 0.0 for uil in user_input.user_input_line_ids])" />
<div> You scored <t t-esc="page_score" /> points in page <t t-esc="p" />.</div>
</t>
I have not tested this code but logically it seems to work fine.
Thank you so much #MICROCOM .. The line that display the score for each page is diplayed more than once ..
The result Of The code - 1
So i added a condition to just display the score of a certain page once (I don't know if it's correct but it works :p ) .. Here is the code :
<!-- First finding which page is related to the survey. This is a bit odd but I couldn't find any other way!! -->
<t t-set="pages" t-value="dict((l.id, l.page_id.title) for l in user_input.user_input_line_ids).values()" />
<!-- Then print the score per each page -->
<t t-set="previous" t-value="void" />
<t t-foreach='pages' t-as='p'>
<t t-if="p != previous">
<t t-set="page_score" t-value="sum([(uil.quizz_mark or 0.0) if uil.page_id.title == p else 0.0 for uil in user_input.user_input_line_ids])" />
<div> You scored <t t-esc="page_score" /> points in page <t t-esc="p" />.</div>
</t>
<t t-set="previous" t-value="p" />
</t>
The result Of The code - 2
Thank you so much for your help #MICROCOM ^^