I need to download files in zip format.
I wrote code that for some reason does not work.
I'm trying to do it with controller
attachment.py
from odoo import api, fields, models, _
from odoo.odoo.exceptions import UserError
class IrAttachment(models.Model):
_inherit = ["ir.attachment", "sale.order.line"]
def download_product_attachments(self):
attachment_id = self.datas
if attachment_id:
return {"type": "ir.actions.act_url",
"url": "/download_attachments?attachment_id={}".format(
",".join([str(elem) for elem in attachment_id.ids])),
"target": "new",
"res_id": self.id,
}
else:
raise UserError("Photos were not found")
contriller/main.py
class DownloadZipFile(http.Controller):
#http.route("/download_attachments", type="http", auth="user", website=True)
def download_attachments_product_routes(self, **data):
"""Method compose data with attachments and send for download."""
attachments_ids = [int(x) for x in data["attachment_id"].split(',')]
attachments_items = request.env["ir.attachment"].search([("id", "in", attachments_ids)])
in_memory = BytesIO()
zip_archive = ZipFile(in_memory, "w")
for attachment in attachments_items:
zip_archive.writestr(attachment.filename, base64.b64decode(attachment.image))
zip_archive.close()
res = http.send_file(in_memory, filename="attachments.zip", as_attachment=True)
return res
When I try to go to the /download_attachments. I get an error
File "/home/user/PycharmProjects/Odoo14/custom/first_model/controllers/main.py", line 32, in download_attachments_product_routes
attachments_ids = [int(x) for x in data["attachment_id"].split(',')]
KeyError: 'attachment_id'
What's wrong with me?
I found a solution to my question.
sale.py
class SaleOrder(models.Model):
_inherit = "sale.order"
attachment = fields.Many2one('ir.attachment')
def download_product_attachments(self):
return {"type": "ir.actions.act_url",
"url": "/download_attachments?res_id={}".format(self.id),
}
controllers/main.py
class DownloadZipFile(http.Controller):
#http.route("/download_attachments/", type="http", auth="user", website=True)
def download_attachments_product_routes(self, **data):
"""Method compose data with attachments and send for download."""
attachments_items = request.env["ir.attachment"].search(
[("res_id", "=", data.get('res_id')), ('res_model', '=', 'sale.order')])
in_memory = BytesIO()
zip_archive = ZipFile(in_memory, "w")
for attachment in attachments_items:
zip_archive.writestr(attachment.name, base64.b64decode(attachment.datas))
zip_archive.close()
res = http.send_file(in_memory, filename="attachments.zip", as_attachment=True)
return res
sale.xml
<record id="view_order_form_inherit" model="ir.ui.view">
<field name="name">sale.order.form</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button name="download_product_attachments" type="object" string="Download all files"/>
</xpath>
</field>
</record>
Related
Create a sale order and add a couple of items. After clicking on the "Confirm" button the delivery "Delivery" will be created. All items from the sale order will be in delivery.
You need to add the express delivery sign to the sale order (bool).
Example: Add 5 items A B C(express delivery) D E(express delivery) to the sale order. After sale order confirmation two deliveries should be created: the first delivery should contain positions A, B and D; the second delivery (express delivery) should contain positions C and E.
I added the button. and now with the debugger I reached the function def _action_launch_stock_rule(self, previous_product_uom_qty=False) in odoo/addons/sale_stock/models/sale_order.py
I don't know what to do next. I can't understand at what stage (in which function) the data is thrown to delivery.
And there I need to write the logic. If we have Express delivery then I have to divide it into two deliveries.
sale.py
class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
express_delivery = fields.Boolean(string='Express delivery')
sale.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_order_form_inherit" model="ir.ui.view">
<field name="name">sale.order.form</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[#name='order_line']/tree/field[#name='qty_delivered']" position="after">
<field name="express_delivery" />
</xpath>
</field>
</record>
</odoo>
Maybe someone can help me understand?
I found a solution to this problem
sale.py
class SaleOrderLine(models.Model):
_name = "sale.order.line"
_inherit = "sale.order.line"
express_delivery = fields.Boolean(string='Express delivery')
def _prepare_procurement_values(self, group_id=False):
res = super(SaleOrderLine, self)._prepare_procurement_values(group_id)
res.update({'express_delivery': self.express_delivery})
return res
stock_move.py
from odoo import models, fields
from itertools import groupby
class StockMoveInherit(models.Model):
_inherit = 'stock.move'
express_delivery = fields.Boolean()
def _search_picking_for_assignation(self):
self.ensure_one()
picking = self.env['stock.picking'].search([
('group_id', '=', self.group_id.id),
('location_id', '=', self.location_id.id),
('location_dest_id', '=', self.location_dest_id.id),
('picking_type_id', '=', self.picking_type_id.id),
('printed', '=', False),
('express_delivery', '=', self.express_delivery), # Добавляю поле
('immediate_transfer', '=', False),
('state', 'in', ['draft', 'confirmed', 'waiting', 'partially_available', 'assigned'])], limit=1)
return picking
def _assign_picking(self):
Picking = self.env['stock.picking']
grouped_moves = groupby(sorted(self, key=lambda m: [f.id for f in m._key_assign_picking()]),
key=lambda m: [m._key_assign_picking(), m.express_delivery])
for group, moves in grouped_moves:
moves = self.env['stock.move'].concat(*list(moves))
new_picking = False
picking = moves[0]._search_picking_for_assignation()
if picking:
if any(picking.partner_id.id != m.partner_id.id or
picking.origin != m.origin for m in moves):
picking.write({
'partner_id': False,
'origin': False,
})
else:
new_picking = True
picking = Picking.create(moves._get_new_picking_values())
picking.write({'express_delivery': moves[0].express_delivery}) # Добавляю поле
moves.write({'picking_id': picking.id})
moves._assign_picking_post_process(new=new_picking)
return True
stock_picking.py
from odoo import fields, models, api
class StockMoveInherit(models.Model):
_name = 'stock.picking'
_inherit = 'stock.picking'
express_delivery = fields.Boolean(default=False)
stock_rule.py
from odoo import api, fields, models
class StockRuleInh(models.Model):
_inherit = 'stock.rule'
def _get_stock_move_values(self, product_id, product_qty, product_uom, location_id, name, origin, company_id,
values):
res = super(StockRuleInh, self)._get_stock_move_values(product_id, product_qty, product_uom, location_id,
name, origin, company_id, values)
res['express_delivery'] = values.get('express_delivery', False)
return res
stock_picking_view.xml
<?xml version="1.0"?>
<odoo>
<record id="stock_picking_view_form_inherit" model="ir.ui.view">
<field name="name">stock.picking.view.form.inherit</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//group/field[#name='origin']" position="after">
<field name="express_delivery" attrs="{'readonly':[True]}" />
</xpath>
</field>
</record>
<record id="vpicktree_inherit" model="ir.ui.view">
<field name="name">stock.picking.tree</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.vpicktree"/>
<field name="arch" type="xml">
<xpath expr="//tree/field[#name='user_id']" position="after">
<field name="express_delivery"/>
</xpath>
</field>
</record>
</odoo>
sale.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_order_form_inherit" model="ir.ui.view">
<field name="name">sale.order.form</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[#name='order_line']/tree/field[#name='qty_delivered']" position="after">
<field name="express_delivery" />
</xpath>
</field>
</record>
</odoo>
i'm using odoo 11. i have create a custom module for attendance modification request which be approuved by the manager of attendance . My goal is to limit the acces to the modification of attendance and when the manager press approuve "hr.attendance" will be modified automatically whith the new values of check_in and check_out (if it's the same employee and same date between "hr.attendance" and my request modification). The problem is when i press approuve there is no error but in the same time there is no modification.
Here is my code. Any idea for help please ?
modification_request.xml
<record model="ir.ui.view" id="view_attendance_modification_request_form">
<field name="name">attendance.modification.request.form</field>
<field name="model">attendance.modification.request</field>
<field name="arch" type="xml">
<form string="Attendance modification Request">
<header>
<field name="state" statusbar_visible="draft,waiting,approved,cancel" widget="statusbar" />
<button name="submit_modification" string="Submit for manager" type="object" class="btn-primary"
attrs="{'invisible': [('state','not in','draft')]}"/>
<button name="modification_approval" type="object" string="Approve" class="oe_highlight"
groups="hr_attendance.group_hr_attendance_manager"
attrs="{'invisible': [('state','not in','waiting')]}"/>
<button name="modification_rejection" type="object" string="Cancel" class="oe_highlight"
groups="hr_attendance.group_hr_attendance_manager"
attrs="{'invisible': [('state','not in','waiting')]}"/>
</header>
<sheet>
<h2>
<group>
<field name="employee_id"/>
</group>
</h2>
<group col="4" colspan="4">
<field name="time_check_in_1"/>
<field name="time_check_out_1"/>
</group>
<label for="note"/>
<field name="note"/>
</sheet>
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"/>
</form>
</field>
</record>
modification_request.py
class AttendanceModificationRequest(models.Model):
_name = 'attendance.modification.request'
_description = 'Attendance modification Request'
_inherit = ['mail.thread', 'mail.activity.mixin']
def _get_employee_id(self):
employee_rec = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
return employee_rec.id
employee_id = fields.Many2one('hr.employee',"Employee", readonly=True,default=_get_employee_id, required=True)
user_id = fields.Many2one('res.users', string='User', track_visibility='onchange', readonly=True,
states={'draft': [('readonly', False)]}, default=lambda self: self.env.user)
state = fields.Selection([
('draft', 'Pending'),
('waiting', 'Waiting for approval'),
('approved', 'Approved'),
('cancel', 'Cancelled')], readonly=True,
help="Gives the state of the attendance request modification .",
default='draft')
modification_date = fields.Date("Date")
time_check_in_1 = fields.Datetime("Check in")
time_check_out_1 = fields.Datetime("Check out")
note = fields.Text("Note")
attendance_id = fields.Many2one('hr.attendance', string='Attendance')
#api.multi
def modification_approval(self):
attend_signin_ids = self.env['hr.attendance']
check_in_date = datetime.strptime(self.time_check_in_1, "%Y-%m-%d %H:%M:%S").date()
check_out_date = datetime.strptime(self.time_check_out_1, "%Y-%m-%d %H:%M:%S").date()
for record in self:
attendance_check_in_date = datetime.strptime(record.attendance_id.check_in, "%Y-%m-%d %H:%M:%S").date()
attendance_check_out_date = datetime.strptime(record.attendance_id.check_out, "%Y-%m-%d %H:%M:%S").date()
if (record.attendance_id.employee_id == self.employee_id) and (check_in_date == attendance_check_in_date):
record.attendance_id.check_in = self.time_check_in_1
record.attendance_id.check_out = self.time_check_out_1
return self.write({
'state': 'approved'
})
since attendance_ids is a group of people. attendance_id = fields.Many2one('hr.attendance', string='Attendance') is an issue.
Because m2o can't refer to a group of people.
attendance_id = fields.One2many('hr.attendance','keyfield_in_hr_attendance' ,string='Attendance') is correct.
And keyfield_in_hr_attendance is a many2one field refer to attendance.modification.request model.
I am trying to generate excel report using Odoo 10. The code is working fine and I am able to do that. But the file does'nt download on a single click. It saves the file and shows download link on wizard. But I dont want this extra step. I want the file to be downloaded in single click. I am sharing my working code below here. Please have a look and suggest me what should be added to make it work in a single click.
xml code:
<div state="get">
<group>
<field name="name" colspan="4" invisible="1"/>
<field name="report" filename="name" colspan="4"/>
</group>
</div>
<button name="generate_xls_report" string="Export XLS" type="object" class="oe_highlight" />
Python Code:
from odoo import fields, models, api, _
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
import xlwt
import base64
import cStringIO
from datetime import datetime
class CasesWizard(models.TransientModel):
_name = "cases.wizard"
_description = "Cases wizard"
case_id = fields.Many2one('project.project', string='Cases')
event_id = fields.Many2one('calendar.event', string='Events')
company_id = fields.Many2one('res.company', string='company id', readonly=True,default=lambda self: self.env.user.company_id.id)
lawyer_id = fields.Many2one('res.users', string='Lawyers')
#partner_id = fields.Many2one('res.partner', string='Clients')
date_from = fields.Date(string='Start Date')
date_to = fields.Date(string='End Date')
state = fields.Selection([('choose', 'choose'), ('get', 'get')],default='choose')
report = fields.Binary('Prepared file', filters='.xls', readonly=True)
name = fields.Char('File Name', size=32)
#api.multi
def generate_xls_report(self):
self.ensure_one()
wb1 = xlwt.Workbook(encoding='utf-8')
ws1 = wb1.add_sheet('Case Event Details')
fp = cStringIO.StringIO()
# Here all excel data and calculations
wb1.save(fp)
out = base64.encodestring(fp.getvalue())
self.write({'state': 'get', 'report': out, 'name':'event_details.xls'})
return {
'type': 'ir.actions.act_window',
'res_model': 'cases.wizard',
'view_mode': 'form',
'view_type': 'form',
'res_id': self.id,
'views': [(False, 'form')],
'target': 'new',
'name': 'Event Details Report'
}
You can achieve that using a web controller.
Add the following method to the wizard model:
#api.multi
def generate_xls_report(self):
self.ensure_one()
return {
'type': 'ir.actions.act_url',
'url': '/web/binary/download_xls_document?model=cases.wizard&id=%s&filename=event_details.xls' % (
self.id),
'target': 'new',
}
Then create the web controller:
class Binary(http.Controller):
#http.route('/web/binary/download_xls_document', type='http', auth="public")
#serialize_exception
def download_xls_document(self, model, id, filename=None, **kw):
Model = request.registry[model]
cr, uid, context = request.cr, request.uid, request.context
wb1 = xlwt.Workbook(encoding='utf-8')
ws1 = wb1.add_sheet('Case Event Details')
fp = cStringIO.StringIO()
# Here all excel data and calculations
wb1.save(fp)
filecontent = fp.getvalue()
if not filecontent:
return request.not_found()
else:
if not filename:
filename = '%s_%s' % (model.replace('.', '_'), id)
return request.make_response(filecontent,
[('Content-Type', 'application/octet-stream'),
('Content-Disposition', content_disposition(filename))])
This should generate an empty xls file.
I have two model:
class Requisition(models.Model):
'''
This module is responsible for all of the requisition related operations.
'''
_name = 'mir.requisition'
_description = 'Mir Requisition'
name = fields.Char('Requisition Name', required=True)
company_id = fields.Many2one('res.company', string='Company')
requisition_line_ids = fields.One2many('mir.requisition.line', 'requisition_id', string='Requisition Line')
class RequisitionLine(models.Model):
'''
This module is responsible for all of the requisition line item related operations.
'''
_name = 'mir.requisition.line'
_description = 'Mir Requisition Line'
product_id = fields.Many2one(
'product.product', 'Product', required=True)
product_qty = fields.Float(
'Quantity',
digits=dp.get_precision('Product Unit of Measure'), default=0, required=True)
product_uom_id = fields.Many2one(
'product.uom', 'Unit of Measure',
oldname='product_uom', required=True)
delivery_date = fields.Date(string='Delivery Date', required=True)
delivery_location = fields.Many2one('stock.location', 'Warehouse Location', required=True)
requisition_id = fields.Many2one('mir.requisition', string='Mir Requisition Reference', index=True, ondelete='cascade')
status = fields.Selection([
('draft', 'Draft'),
('in_progress', 'In Progress'),
('approved', 'Approved'),
('cancel', 'Cancel'),
], 'draft')
#api.onchange('product_id')
def _product_onchange(self):
product = self.product_id
self.product_uom_id = self.product_id.uom_id.id
return {'domain': {'product_uom': [('category_id', '=', product.uom_id.category_id.id)]}}
and view is:
<!--Requisition Tree View-->
<record id="mir_requisition_tree" model="ir.ui.view">
<field name="name">Requisition</field>
<field name="model">mir.requisition</field>
<field name="arch" type="xml">
<tree string="Requisition">
<field name="name"/>
<field name="company_id"/>
<field name="create_uid" string="Created By"/>
<field name="create_date" string="Created Date"/>
</tree>
</field>
</record>
Currently this view showing data form parent model mir.requisition. But i want to display both model data in a single view. Any help will be appreciated.
You should take a look the invoice_form definition, it works with 'account.invoice' and 'account.invoice.line' models. Its something like this:
<!--Requisition Tree View-->
<record id="mir_requisition_tree" model="ir.ui.view">
<field name="name">Requisition</field>
<field name="model">mir.requisition</field>
<field name="arch" type="xml">
<form string="Requisition">
<field name="name"/>
<field name="company_id"/>
<field name="create_uid" string="Created By"/>
<field name="create_date" string="Created Date"/>
<field name="requisition_line_ids" nolabel="1" widget="one2many_list" >
<tree string="Requisition Lines" editable="bottom">
<!-- 'mir.requisition.line' fields -->
</tree>
</field>
</form>
</field>
</record>
I hope this answer can be helful for you.
I have to make Purchase Order Line as editable in Odoo 8. Currently, the field order_line in Purchase.Order Model has modifier as below:
'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines',
states={'approved':[('readonly',True)],
'done':[('readonly',True)]},
copy=True)
So the states is readonly if approved or done. I want to remove this. I tried with below:
<field name="order_line" position="attributes">
<attribute name="readonly">0</attribute>
</field>
Also,
<xpath expr="//field[#name='order_line']" position="attributes">
<attribute name="readonly">0</attribute>
</xpath>
But It does not work.
Please help
Thanks,
UPDATE:
class PurchaseOrder(models.Model):
'''
classdocs
'''
_name = 'purchase.order'
_inherit = 'purchase.order'
total_cases = fields.Integer('Total Cases', default=None)
appointment_number = fields.Char('Appointment Number', default=None)
order_line = fields.One2many('purchase.order.line', 'order_id', 'Order Lines', copy=True)
I overrided the field order_line as above, But nothing happens
Just inherit from the model and define the field again to override it, then you can remove states totally
from openerp import fields, models
class custom_purchase_order(models.Model):
_inherit = 'purchase.order'
order_line = fields.One2many('purchase.order.line', 'order_id', 'Order Lines', states={}, copy=True)