migration
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) PySquad Informetics (<https://www.pysquad.com/>).
|
||||
#
|
||||
# For Module Support : contact@pysquad.com
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
# Module Information
|
||||
"name": "Dynamic Report",
|
||||
"version": "18.0.1.0.0",
|
||||
"category": "Odex30-base",
|
||||
"description": "Export to pdf and xls in one click",
|
||||
"summary": """
|
||||
Dynamic Report
|
||||
Export Tree View PDF
|
||||
Export Tree View XLSx
|
||||
Creating Fully Dynamically Pdf & XLS report.
|
||||
""",
|
||||
|
||||
# Author
|
||||
"author": "Pysquad Informatics LLP",
|
||||
"website": "https://www.pysquad.com",
|
||||
"license": "LGPL-3",
|
||||
|
||||
# Dependencies
|
||||
"depends": ["base"],
|
||||
|
||||
# Data File
|
||||
"data": [
|
||||
'security/ir.model.access.csv',
|
||||
'report/dynamic_report.xml',
|
||||
'views/dynamic_report_configure.xml',
|
||||
],
|
||||
'images': [
|
||||
'static/description/banner_img.png',
|
||||
],
|
||||
|
||||
# Technical Specif.
|
||||
'installable': True,
|
||||
'application': False,
|
||||
'auto_install': False,
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import dynamic_report_configure
|
||||
from . import inherit_ir_actions
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
import xlwt
|
||||
import base64
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
class DynamicReportConfigure(models.Model):
|
||||
_name = 'dynamic.report.configure'
|
||||
_rec_name = 'name'
|
||||
|
||||
name = fields.Char('Name')
|
||||
model_id = fields.Many2one('ir.model', 'Model', domain="[('transient', '=', False)]")
|
||||
dynamic_field_id = fields.One2many('dynamic.report.field', 'dynamic_configure_id', 'Dynamic Field')
|
||||
is_action_created = fields.Boolean('Is Created?', default=False)
|
||||
server_action_id = fields.Many2one('ir.actions.server', 'Server id')
|
||||
report_type = fields.Selection([('pdf', 'PDF'), ('xls', 'XLS')], default='pdf', string="Report Type")
|
||||
|
||||
@api.onchange('model_id')
|
||||
def _onchange_model_id(self):
|
||||
if self.dynamic_field_id:
|
||||
self.dynamic_field_id.unlink()
|
||||
|
||||
def create_dynamic_pdf_report(self, server):
|
||||
server_id = self.env['ir.actions.server'].search([('dynamic_report_id', '=', server)])
|
||||
dynamic_report_id = server_id.dynamic_report_id.dynamic_field_id.filtered(lambda a: a.field_id)
|
||||
Heading_values = [item.field_id.field_description for item in dynamic_report_id]
|
||||
|
||||
record_data = []
|
||||
total_data = []
|
||||
active_model_id = self.env[self._context.get('active_model')].browse(self._context.get('active_ids'))
|
||||
for model in active_model_id:
|
||||
temp = []
|
||||
for item in dynamic_report_id:
|
||||
if item.field_type == 'selection':
|
||||
state = dict(model._fields[item.field_name_id].selection).get(model[item.field_name_id])
|
||||
temp.append(state)
|
||||
elif item.field_type == 'datetime':
|
||||
date = getattr(model, item.field_name_id)
|
||||
temp.append(str(date.date()) if date else '')
|
||||
elif item.field_type == 'date':
|
||||
temp.append(str(getattr(model, item.field_name_id) or ''))
|
||||
elif item.field_type == 'many2one':
|
||||
temp_value = getattr(model, item.field_name_id)
|
||||
value = getattr(temp_value, temp_value._rec_name)
|
||||
temp.append(value)
|
||||
else:
|
||||
temp.append(getattr(model, item.field_name_id))
|
||||
|
||||
if len(total_data) < len(Heading_values):
|
||||
if item.is_sum_calc:
|
||||
m_value = sum(active_model_id.mapped(item.field_name_id))
|
||||
total_data.append(m_value)
|
||||
else:
|
||||
total_data.append('')
|
||||
|
||||
record_data.append(temp)
|
||||
data = {
|
||||
'report_name': server_id.dynamic_report_id.name,
|
||||
'model_id': server_id.dynamic_report_id.model_id.model,
|
||||
'name': server_id.dynamic_report_id.name,
|
||||
'table_heading': Heading_values,
|
||||
'record_data': record_data,
|
||||
'total_data': total_data
|
||||
}
|
||||
if server_id.dynamic_report_id.report_type == 'pdf':
|
||||
return self.env.ref('ps_dynamic_report.action_dynamic_report_print').report_action(self, data=data)
|
||||
else:
|
||||
workbook = xlwt.Workbook(encoding='utf-8', style_compression=0)
|
||||
fl = BytesIO()
|
||||
worksheet = workbook.add_sheet('Report Details', cell_overwrite_ok=True)
|
||||
font = xlwt.Font()
|
||||
|
||||
xlwt.add_palette_colour("custom_colour", 0x21)
|
||||
workbook.set_colour_RGB(0x21, 245, 184, 103)
|
||||
style1 = xlwt.easyxf("pattern: pattern solid, fore_colour custom_colour; alignment: vert centre, horiz center; font: name Arial, bold True;")
|
||||
style2 = xlwt.easyxf("pattern: pattern solid, fore_colour silver_ega; alignment: horizontal center; font: name Arial, bold True;")
|
||||
style3 = xlwt.easyxf('alignment: horizontal center;')
|
||||
style4 = xlwt.easyxf('alignment: horizontal center; font: name Arial, bold True;')
|
||||
|
||||
cell_value = xlwt.XFStyle()
|
||||
cell_value.font = font
|
||||
|
||||
worksheet.write_merge(0, 1, 0, len(Heading_values)-1, server_id.dynamic_report_id.name or '', style1)
|
||||
|
||||
row = 3
|
||||
col = 0
|
||||
for heading in Heading_values:
|
||||
worksheet.col(col).width = 500 * 12
|
||||
worksheet.write(row, col, heading, style2)
|
||||
col += 1
|
||||
|
||||
row = 4
|
||||
col = 0
|
||||
for data in record_data:
|
||||
for rec in data:
|
||||
worksheet.write(row, col, rec, style3)
|
||||
col += 1
|
||||
col = 0
|
||||
row += 1
|
||||
|
||||
col = 0
|
||||
for total in total_data:
|
||||
worksheet.write(row, col, total, style4)
|
||||
col += 1
|
||||
|
||||
filename = server_id.dynamic_report_id.name or "Report Detail"
|
||||
workbook.save(fl)
|
||||
fl.seek(0)
|
||||
test = base64.encodebytes(fl.read())
|
||||
attach_vals = {
|
||||
'name': '%s.xlsx' % (filename),
|
||||
'datas': test,
|
||||
}
|
||||
doc_id = self.env['ir.attachment'].create(attach_vals)
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': 'web/content/%s?download=true' % (doc_id.id),
|
||||
'target': 'self',
|
||||
}
|
||||
|
||||
def create_server_action(self):
|
||||
if not self.dynamic_field_id:
|
||||
raise UserError(_("Please Select few Fields!!"))
|
||||
vals = {
|
||||
'name': self.name if self.name else 'Custom Action',
|
||||
'model_id': self.model_id.id,
|
||||
'model_name': self.model_id.name,
|
||||
'state': 'code',
|
||||
'binding_model_id': self.model_id.id,
|
||||
'binding_view_types': 'list',
|
||||
'dynamic_report_id': self.id,
|
||||
'code': "action = env['dynamic.report.configure'].create_dynamic_pdf_report(server={server_id})".format(server_id=self.id)
|
||||
}
|
||||
server_action_id = self.env['ir.actions.server'].create(vals)
|
||||
self.server_action_id = server_action_id.id
|
||||
self.is_action_created = True
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
||||
|
||||
def remove_server_action(self):
|
||||
if self.is_action_created and self.server_action_id:
|
||||
self.server_action_id.unlink()
|
||||
self.is_action_created = False
|
||||
|
||||
|
||||
class DynamicReportField(models.Model):
|
||||
_name = 'dynamic.report.field'
|
||||
|
||||
field_id = fields.Many2one('ir.model.fields', 'Field')
|
||||
sequence = fields.Integer('..', help="Gives the sequence order when displaying a list O2m.")
|
||||
field_name_id = fields.Char(related='field_id.name', string='Target Model Name', readonly=True)
|
||||
field_type = fields.Selection(related='field_id.ttype', string='Field Type')
|
||||
dynamic_configure_id = fields.Many2one('dynamic.report.configure', 'Reference')
|
||||
is_sum_calc = fields.Boolean("Sum")
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(DynamicReportField, self).default_get(fields)
|
||||
if self._context:
|
||||
context_keys = self._context.keys()
|
||||
next_sequence = 1
|
||||
if 'dynamic_field_id' in context_keys:
|
||||
if len(self._context.get('dynamic_field_id')) > 0:
|
||||
next_sequence = len(self._context.get('dynamic_field_id')) + 1
|
||||
res.update({'sequence': next_sequence})
|
||||
return res
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class InheritIrActionsServer(models.Model):
|
||||
_inherit = 'ir.actions.server'
|
||||
|
||||
dynamic_report_id = fields.Many2one('dynamic.report.configure', 'Dynamic Reference')
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="report_dynamic">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.external_layout">
|
||||
<style>
|
||||
table {
|
||||
font-family: arial, sans-serif;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td, th {
|
||||
border: 1px solid #dddddd;
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #dddddd;
|
||||
}
|
||||
</style>
|
||||
<div class="page">
|
||||
<center>
|
||||
<h3>
|
||||
<strong style="text-align:center;">
|
||||
<t t-esc="report_name"/>
|
||||
</strong>
|
||||
</h3>
|
||||
</center>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<t t-foreach="table_heading" t-as="line">
|
||||
<th name="th_description" class="text-left">
|
||||
<span t-esc="line"/>
|
||||
</th>
|
||||
</t>
|
||||
</tr>
|
||||
<tbody>
|
||||
<t t-foreach="record_data" t-as="record">
|
||||
<tr>
|
||||
<t t-foreach="record" t-as="rec">
|
||||
<td>
|
||||
<span t-esc="rec"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</t>
|
||||
<tr>
|
||||
<t t-foreach="total_data" t-as="total">
|
||||
<td>
|
||||
<span t-esc="total"/>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<record id="action_dynamic_report_print" model="ir.actions.report">
|
||||
<field name="name">Report</field>
|
||||
<field name="model">dynamic.report.configure</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">ps_dynamic_report.report_dynamic</field>
|
||||
<field name="report_file">ps_dynamic_report.report_dynamic</field>
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_dynamic_report_configure,dynamic_report_configure,model_dynamic_report_configure,,1,1,1,1
|
||||
access_dynamic_report_field,dynamic_report_field,model_dynamic_report_field,,1,1,1,1
|
||||
|
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 185 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
|
@ -0,0 +1,230 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
|
||||
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
<link href="/multi_company_selector/static/src/css/demo.css" media="screen, print" rel="stylesheet"
|
||||
type="text/css"/>
|
||||
<title>Hello, web</title>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section style="padding-bottom:1vh;margin-top: 8vh;">
|
||||
<header>
|
||||
|
||||
<div class="navbar navbar-light shadow-sm " style="border-bottom: 1px solid #d5d5d5;">
|
||||
<div class="container d-flex justify-content-between">
|
||||
<div class="col-md-3">
|
||||
<a href="#" class="navbar-brand d-flex align-items-center">
|
||||
<img class="img-fluid d-block mx-auto" src="images/logo.jpeg">
|
||||
</a>
|
||||
</div>
|
||||
<div class="my-3 d-flex align-items-center">
|
||||
<div style="background-color:#7C7BAD !important; color:#fff !important; font-weight:600 !important; padding:5px 15px 8px !important; margin:0 5px !important">
|
||||
<i class="fa fa-check mr-1"></i>Community
|
||||
</div>
|
||||
<div style="background-color:#8F4D7D !important; color:#fff !important; font-weight:600 !important; padding:5px 15px 8px !important; margin:0 5px !important">
|
||||
<i class="fa fa-check mr-1"></i>Enterprise
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center pt-5">
|
||||
<div class="col-md-10">
|
||||
<strong>
|
||||
<p>
|
||||
This Module helps you to create Fully Dynamically Pdf & XLS report. You Can also Create Multiple
|
||||
Action of Same Model and Select Fields according to Show in Report Just one Click.
|
||||
</p>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding:3.5rem 1.5rem !important; background-color:#ffffff !important; padding-bottom:1vh">
|
||||
<div class="conntainer">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<img class="img-fluid d-block mx-auto border border-secondry"
|
||||
src="/images/ps_image1.png">
|
||||
<h5 style="padding-top:3vh" class="text-center">Go to Dynamic Report Configure under(settings)</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding:3.5rem 1.5rem !important; background-color:#ffffff !important; padding-bottom:1vh">
|
||||
<div class="conntainer">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<img class="img-fluid d-block mx-auto border border-secondry"
|
||||
src="/images/ps_image2.png">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h5 style="padding-top:3vh; text-align: left !important;" class="text-center">Create a one
|
||||
record</h5>
|
||||
<ul>
|
||||
<li>Select the model which you want to create of his Server action</li>
|
||||
<li>Choose a Report Type PDF or XLS</li>
|
||||
<li>And if you want a total of any fields, Just enable a Sum checkbox.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding:3.5rem 1.5rem !important; background-color:#ffffff !important; padding-bottom:1vh">
|
||||
<div class="conntainer">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<img class="img-fluid d-block mx-auto border border-secondry"
|
||||
src="/images/ps_image3.png">
|
||||
<h5 style="padding-top:3vh" class="text-center">Click on Create Action Button to create a server
|
||||
action.</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding:3.5rem 1.5rem !important; background-color:#ffffff !important; padding-bottom:1vh">
|
||||
<div class="conntainer">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<img class="img-fluid d-block mx-auto border border-secondry"
|
||||
src="/images/ps_image4.png">
|
||||
<h5 style="padding-top:3vh" class="text-center">Above you can see a action is created. Now, Click on it
|
||||
to Download Report</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding:3.5rem 1.5rem !important; background-color:#ffffff !important; padding-bottom:1vh">
|
||||
<div class="conntainer">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<img class="img-fluid d-block mx-auto border border-secondry"
|
||||
src="/images/ps_image5.png">
|
||||
<h5 style="padding-top:3vh" class="text-center">Pdf report.</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding:3.5rem 1.5rem !important; background-color:#ffffff !important; padding-bottom:1vh">
|
||||
<div class="conntainer">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<img class="img-fluid d-block mx-auto border border-secondry"
|
||||
src="/images/ps_image6.png">
|
||||
<h5 style="padding-top:3vh" class="text-center">To Create a XLS report action.</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding:3.5rem 1.5rem !important; background-color:#ffffff !important; padding-bottom:1vh">
|
||||
<div class="conntainer">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<img class="img-fluid d-block mx-auto border border-secondry"
|
||||
src="/images/ps_image7.png">
|
||||
<h5 style="padding-top:3vh" class="text-center"></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding:3.5rem 1.5rem !important; background-color:#ffffff !important; padding-bottom:1vh">
|
||||
<div class="conntainer">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<img class="img-fluid d-block mx-auto border border-secondry"
|
||||
src="/images/ps_image8.png">
|
||||
<h5 style="padding-top:3vh" class="text-center">Xls Report</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style="padding:3.5rem 1.5rem">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="text-left" style="width:100%">
|
||||
<h2 style="letter-spacing:2px; font-weight:800;font-size: 2rem; color:#000; text-transform:uppercase; font-family:'Montserrat', sans-serif">
|
||||
HELP AND SUPPORT
|
||||
</h2>
|
||||
<span class="o_gradient" style="width:280px; height:2px; display:inline-block; border-radius:2px; background: linear-gradient(
|
||||
150deg, #875a7b 20%, #62495b 80%) !important;"></span>
|
||||
<div class="row" style="border-bottom: 1px solid #000; padding-bottom: 9vh;">
|
||||
<div class="col-lg-12 d-flex justify-content-center">
|
||||
<span class="btn btn-danger btn-lg" style="margin-top:4vh;background-color:#e86874;"><i
|
||||
class="fa fa-question-circle align-middle"></i> For any Query</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row" style="padding-top: 1vh; background-color:#DAD9DF">
|
||||
|
||||
|
||||
<div class="col-lg-4 text-center">
|
||||
<img width="40px" align="center" height="40px" src="images/gmail.png">
|
||||
<h4 style="padding-top:10px">Write a mail to us</h4>
|
||||
<h3 style="color: #75b1ab;"><a href="contact@pysquad.com" target="_blank"
|
||||
style="color: #393185;">contact@pysquad.com</a></h3>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-lg-4 text-center">
|
||||
<img width="40px" align="center" height="40px" src="images/whatsapp.png">
|
||||
<h4 style="padding-top:10px">Write a text To us on WhatsApp</h4>
|
||||
<h3><a href="http://wa.me/919825241294" target="_blank" style="color: #393185;">+91
|
||||
9825241294</a></h3>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-lg-4 text-center">
|
||||
<img width="40px" align="center" height="40px" src="images/web.png">
|
||||
<h4 style="padding-top:10px">Visit Our Wesite</h4>
|
||||
<h3><a href="https://pysquad.com/" target="_blank"
|
||||
style="color: #393185;">www.pysquad.com</a></h3>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Optional JavaScript -->
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
|
||||
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
|
||||
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
|
||||
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
|
||||
crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="dynamic_report_configure_form_view" model="ir.ui.view">
|
||||
<field name="name">dynamic.report.configure.view.form</field>
|
||||
<field name="model">dynamic.report.configure</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Dynamic Report Configure">
|
||||
<header>
|
||||
<button name="create_server_action" string="Create Action" type="object" class="oe_highlight"
|
||||
invisible="is_action_created != False"/>
|
||||
<button name="remove_server_action" string="Remove Action" type="object"
|
||||
invisible="is_action_created == False"/>
|
||||
</header>
|
||||
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name" required="1" readonly="is_action_created != False"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<!-- ✅ حذف context المشكل -->
|
||||
<field name="model_id" required="1" readonly="is_action_created != False"/>
|
||||
<field name="is_action_created" invisible="1"/>
|
||||
<field name="server_action_id" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="report_type" required="1" widget="radio" options="{'horizontal': true}" readonly="is_action_created != False"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="dynamic_field_id" context="{'dynamic_field_id':dynamic_field_id}" nolabel="1" readonly="is_action_created != False" invisible="model_id == False">
|
||||
<list string="Dynamic Field" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="field_id" string="Field Name" options="{'no_create': True}"
|
||||
domain="[('model_id', '=', parent.model_id), ('ttype', 'not in' , ['one2many', 'many2many'])]"/>
|
||||
<field name="field_name_id" string="Technical name"/>
|
||||
<field name="field_type" string="Field Type"/>
|
||||
<field name="dynamic_configure_id" invisible="1"/>
|
||||
<field name="is_sum_calc" invisible="field_type not in ['float', 'integer', 'monetary']"/>
|
||||
</list>
|
||||
<form>
|
||||
<group>
|
||||
<group>
|
||||
<field name="field_id" string="Field Name"/>
|
||||
<field name="field_name_id" string="Technical Name"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="field_type"/>
|
||||
<field name="dynamic_configure_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="dynamic_report_configure_tree_view" model="ir.ui.view">
|
||||
<field name="name">dynamic.report.configure.view.tree</field>
|
||||
<field name="model">dynamic.report.configure</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Configuration">
|
||||
<field name="name" string="Name"/>
|
||||
<field name="model_id" string="Model Name"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_dynamic_report_configure" model="ir.actions.act_window">
|
||||
<field name="name">Dynamic Report</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">dynamic.report.configure</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_dynamic_report_configure"
|
||||
name="Dynamic Report"
|
||||
id="menu_dynamic_report_configure"
|
||||
parent="base.reporting_menuitem"
|
||||
sequence="10"/>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
====================
|
||||
Customized List View
|
||||
====================
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:3a84bbcf7504991f8ea35e9c11270c99debf913dad0f53c5bc0c402e10372871
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--ux-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/server-ux/tree/14.0/customized_list_view
|
||||
:alt: OCA/server-ux
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/server-ux-14-0/server-ux-14-0-customized_list_view
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||||
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-ux&target_branch=14.0
|
||||
:alt: Try me on Runboat
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
Using this module you can add new fields into list views without having deep odoo
|
||||
technical knowledge.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
* Enable debug mode.
|
||||
* Go to Settings.
|
||||
* Click Customized List Views in the top menu.
|
||||
* Create new record.
|
||||
* Select model and list view.
|
||||
* Create new line. Select which field you want to add, before or after each field.
|
||||
* Select widget and optional if required. Optional can be 'show' or 'hide'.
|
||||
* Save record and click Apply.
|
||||
* Open list view you modified. You will see new field there.
|
||||
* You can click Restore Original to restore original view.
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-ux/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/server-ux/issues/new?body=module:%20customized_list_view%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
* Ilyas
|
||||
* Ooops
|
||||
|
||||
Contributors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* `Ooops404 <https://www.ooops404.com>`__:
|
||||
|
||||
* Ilyas <irazor147@gmail.com>
|
||||
|
||||
Maintainers
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
.. |maintainer-ilyasProgrammer| image:: https://github.com/ilyasProgrammer.png?size=40px
|
||||
:target: https://github.com/ilyasProgrammer
|
||||
:alt: ilyasProgrammer
|
||||
|
||||
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|
||||
|
||||
|maintainer-ilyasProgrammer|
|
||||
|
||||
This module is part of the `OCA/server-ux <https://github.com/OCA/server-ux/tree/14.0/customized_list_view>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
from . import models
|
||||
from .hooks import uninstall_hook
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "Customized List View",
|
||||
"summary": "Add fields into list view",
|
||||
"version": "18.0.1.0.0",
|
||||
"category": "Usability",
|
||||
"website": "https://github.com/OCA/server-ux",
|
||||
"author": "Ilyas, Ooops, Odoo Community Association (OCA)",
|
||||
"maintainers": ["ilyasProgrammer"],
|
||||
"data": ["views/views.xml", "security/ir.model.access.csv"],
|
||||
"depends": ["web_domain_field"],
|
||||
"license": "AGPL-3",
|
||||
"installable": True,
|
||||
"uninstall_hook": "uninstall_hook",
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import SUPERUSER_ID, api
|
||||
|
||||
|
||||
def uninstall_hook(cr, registry):
|
||||
# Restore all views
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
customizations = env["custom.list.view"].with_context(active_test=False).search([])
|
||||
for cust in customizations:
|
||||
cust.button_roll_back()
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * web_tree_customized_field_list
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 14.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2024-05-03 19:36+0000\n"
|
||||
"Last-Translator: Francesco Foresti <francesco.foresti@ooops404.com>\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.17\n"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__active
|
||||
msgid "Active"
|
||||
msgstr "Attivo"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model_terms:ir.ui.view,arch_db:web_tree_customized_field_list.view_custom_list_view_form
|
||||
msgid "Apply"
|
||||
msgstr "Applica"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__create_uid
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Creato da"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__create_date
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Creato il"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model,name:web_tree_customized_field_list.model_custom_list_view
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__custom_list_view_id
|
||||
msgid "Custom List View"
|
||||
msgstr "Vista lista personalizzata"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model,name:web_tree_customized_field_list.model_custom_list_view_line
|
||||
msgid "Custom List View Line"
|
||||
msgstr "Riga lista vista personalizzata"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model_terms:ir.ui.view,arch_db:web_tree_customized_field_list.view_custom_list_view_form
|
||||
msgid "Customized List View"
|
||||
msgstr "Vista lista personalizzata"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.actions.act_window,name:web_tree_customized_field_list.action_custom_list_view_tree
|
||||
#: model:ir.ui.menu,name:web_tree_customized_field_list.menu_custom_list_view_tree
|
||||
msgid "Customized List Views"
|
||||
msgstr "Viste lista personalizzate"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__display_name
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__display_name
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_module_module__display_name
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_ui_view__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Nome visualizzato"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__fields_domain
|
||||
msgid "Fields Domain"
|
||||
msgstr "Dominio campi"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_module_module__id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_ui_view__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__label
|
||||
msgid "Label"
|
||||
msgstr "Etichetta"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__archive_uid
|
||||
msgid "Last Archived by"
|
||||
msgstr "Ultima archiviazione di"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__archive_date
|
||||
msgid "Last Archived on"
|
||||
msgstr "Ultima archiviazione il"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view____last_update
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line____last_update
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_module_module____last_update
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_ir_ui_view____last_update
|
||||
msgid "Last Modified on"
|
||||
msgstr "Ultima modifica il"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__write_uid
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Ultimo aggiornamento di"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__write_date
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Ultimo aggiornamento il"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__line_ids
|
||||
msgid "Line"
|
||||
msgstr "Riga"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__list_view_id
|
||||
msgid "List View"
|
||||
msgstr "Vista lista"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__model_id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__model_name
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__model_id
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__model_name
|
||||
msgid "Model"
|
||||
msgstr "Modello"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model,name:web_tree_customized_field_list.model_ir_module_module
|
||||
msgid "Module"
|
||||
msgstr "Modulo"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__name
|
||||
msgid "Name"
|
||||
msgstr "Nome"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__field_id
|
||||
msgid "New Field"
|
||||
msgstr "Nuovo campo"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.constraint,message:web_tree_customized_field_list.constraint_custom_list_view_unique_model_list_view_rec
|
||||
msgid ""
|
||||
"Only one record per view is allowed. Please modify existing record instead."
|
||||
msgstr ""
|
||||
"Solo un record per vista è permesso. Modifica invece il record esistente."
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__optional
|
||||
msgid "Optional"
|
||||
msgstr "Opzionale"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view__original_arch
|
||||
msgid "Original Arch"
|
||||
msgstr "Arch originale"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__after
|
||||
msgid "Place After"
|
||||
msgstr "Inserisci dopo di"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__before
|
||||
msgid "Place Before"
|
||||
msgstr "Inserisci prima di"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model_terms:ir.ui.view,arch_db:web_tree_customized_field_list.view_custom_list_view_form
|
||||
msgid "Restore Original"
|
||||
msgstr "Ripristina originale"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model,name:web_tree_customized_field_list.model_ir_ui_view
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields,field_description:web_tree_customized_field_list.field_custom_list_view_line__use_widget
|
||||
msgid "Widget"
|
||||
msgstr "Widget"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__use_widget__color
|
||||
msgid "color"
|
||||
msgstr "color"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__optional__hide
|
||||
msgid "hide"
|
||||
msgstr "hide"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__use_widget__many2many_tags
|
||||
msgid "many2many_tags"
|
||||
msgstr "many2many_tags"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__use_widget__monetary
|
||||
msgid "monetary"
|
||||
msgstr "monetary"
|
||||
|
||||
#. module: web_tree_customized_field_list
|
||||
#: model:ir.model.fields.selection,name:web_tree_customized_field_list.selection__custom_list_view_line__optional__show
|
||||
msgid "show"
|
||||
msgstr "show"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
from . import custom_list
|
||||
from . import customer_list_view_line
|
||||
from . import ir_ui_view
|
||||
from . import ir_module
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class CustomListView(models.Model):
|
||||
_name = "custom.list.view"
|
||||
_description = "Custom List View"
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
name = fields.Char(required=True)
|
||||
model_id = fields.Many2one("ir.model", required=True, ondelete="cascade")
|
||||
model_name = fields.Char(related="model_id.model", store=True)
|
||||
list_view_id = fields.Many2one("ir.ui.view", required=True)
|
||||
line_ids = fields.One2many("custom.list.view.line", "custom_list_view_id")
|
||||
original_arch = fields.Text(readonly=True)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
"unique_model_list_view_rec",
|
||||
"UNIQUE(list_view_id)",
|
||||
"Only one record per view is allowed. "
|
||||
"Please modify existing record instead.",
|
||||
)
|
||||
]
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
recs = super().create(vals_list)
|
||||
for rec in recs:
|
||||
rec.original_arch = rec.list_view_id.arch
|
||||
return recs
|
||||
|
||||
def button_apply_changes(self):
|
||||
self.ensure_one()
|
||||
doc = etree.XML(self.original_arch)
|
||||
for mod_line in self.line_ids:
|
||||
target = mod_line.before and mod_line.before.name or mod_line.after.name
|
||||
for node in doc.xpath("/tree/field[@name='%s']" % target):
|
||||
node_string = (
|
||||
"<field name='%s' widget='%s' optional='%s' string='%s'/>"
|
||||
% (
|
||||
mod_line.field_id.name,
|
||||
mod_line.use_widget or "",
|
||||
mod_line.optional or "",
|
||||
mod_line.label or mod_line.field_id.field_description,
|
||||
)
|
||||
)
|
||||
new_node = etree.fromstring(node_string)
|
||||
if mod_line.before:
|
||||
node.addprevious(new_node)
|
||||
else:
|
||||
node.addnext(new_node)
|
||||
new_arch = etree.tostring(doc, encoding="unicode").replace("\t", "")
|
||||
self.list_view_id.arch = new_arch
|
||||
|
||||
def button_roll_back(self):
|
||||
self.ensure_one()
|
||||
doc = etree.XML(self.original_arch)
|
||||
new_arch = etree.tostring(doc, encoding="unicode").replace("\t", "")
|
||||
self.list_view_id.arch = new_arch
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
import json
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class CustomListViewLine(models.Model):
|
||||
_name = "custom.list.view.line"
|
||||
_description = "Custom List View Line"
|
||||
|
||||
custom_list_view_id = fields.Many2one("custom.list.view")
|
||||
model_id = fields.Many2one(related="custom_list_view_id.model_id")
|
||||
model_name = fields.Char(related="custom_list_view_id.model_name")
|
||||
field_id = fields.Many2one("ir.model.fields", "New Field")
|
||||
after = fields.Many2one("ir.model.fields", "Place After")
|
||||
before = fields.Many2one("ir.model.fields", "Place Before")
|
||||
optional = fields.Selection([("show", "show"), ("hide", "hide")])
|
||||
label = fields.Char()
|
||||
use_widget = fields.Selection(
|
||||
[
|
||||
("many2many_tags", "many2many_tags"),
|
||||
("color", "color"),
|
||||
("monetary", "monetary"),
|
||||
],
|
||||
string="Widget",
|
||||
default=False,
|
||||
)
|
||||
fields_domain = fields.Char(
|
||||
compute="_compute_fields_domain",
|
||||
readonly=True,
|
||||
store=False,
|
||||
)
|
||||
|
||||
@api.depends("custom_list_view_id.list_view_id")
|
||||
def _compute_fields_domain(self):
|
||||
for rec in self:
|
||||
arch = (
|
||||
rec.custom_list_view_id.original_arch
|
||||
or rec.custom_list_view_id.list_view_id.arch
|
||||
)
|
||||
doc = etree.XML(arch)
|
||||
nodes = doc.xpath("//tree//field")
|
||||
field_names = []
|
||||
for item in nodes:
|
||||
field_names.append(item.attrib["name"])
|
||||
field_ids = self.env["ir.model.fields"].search(
|
||||
[
|
||||
("model", "=", rec.custom_list_view_id.model_name),
|
||||
("name", "in", field_names),
|
||||
]
|
||||
)
|
||||
rec.fields_domain = json.dumps([("id", "in", field_ids.ids)])
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import models
|
||||
|
||||
|
||||
class Module(models.Model):
|
||||
_inherit = "ir.module.module"
|
||||
|
||||
def _button_immediate_function(self, function):
|
||||
res = super(Module, self)._button_immediate_function(function)
|
||||
clv_model = self.env["ir.model"]._get("custom.list.view")
|
||||
if not clv_model:
|
||||
# case when customized_list_view was uninstalled
|
||||
# views will be restored in the uninstall_hook
|
||||
return res
|
||||
line_mods = self.env["custom.list.view.line"].search([("field_id", "=", False)])
|
||||
if line_mods:
|
||||
# fields was deleted during modules operation
|
||||
custom_views = line_mods.mapped("custom_list_view_id")
|
||||
line_mods.unlink()
|
||||
custom_views.button_apply_changes()
|
||||
return res
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import models
|
||||
|
||||
|
||||
class IrUiView(models.Model):
|
||||
_inherit = "ir.ui.view"
|
||||
|
||||
def name_get(self):
|
||||
return [(rec.id, rec.xml_id) for rec in self]
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
* `Ooops404 <https://www.ooops404.com>`__:
|
||||
|
||||
* Ilyas <irazor147@gmail.com>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Using this module you can add new fields into list views without having deep odoo
|
||||
technical knowledge.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
* Enable debug mode.
|
||||
* Go to Settings.
|
||||
* Click Customized List Views in the top menu.
|
||||
* Create new record.
|
||||
* Select model and list view.
|
||||
* Create new line. Select which field you want to add, before or after each field.
|
||||
* Select widget and optional if required. Optional can be 'show' or 'hide'.
|
||||
* Save record and click Apply.
|
||||
* Open list view you modified. You will see new field there.
|
||||
* You can click Restore Original to restore original view.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
clw1,clw1,model_custom_list_view,base.group_system,1,1,1,1
|
||||
clwl1,clwl1,model_custom_list_view_line,base.group_system,1,1,1,1
|
||||
|
|
After Width: | Height: | Size: 9.2 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from . import test_customized_list_view
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
# Copyright 2024 ooops404
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestCustomizedListView(common.SavepointCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestCustomizedListView, cls).setUpClass()
|
||||
cls.view_model = cls.env["ir.ui.view"].sudo()
|
||||
cls.users_model = cls.env["ir.model"]._get("res.users")
|
||||
cls.view_users_tree = cls.env.ref("base.view_users_tree")
|
||||
|
||||
def test_all_customized_list_view(self):
|
||||
new_view = self.view_users_tree.copy()
|
||||
# Lets create some custom.list.view records
|
||||
login_field = self.env["ir.model.fields"].search(
|
||||
[
|
||||
("model_id", "=", self.users_model.id),
|
||||
("name", "=", "login"),
|
||||
]
|
||||
)
|
||||
date_field = self.env["ir.model.fields"].search(
|
||||
[
|
||||
("model_id", "=", self.users_model.id),
|
||||
("name", "=", "create_date"),
|
||||
]
|
||||
)
|
||||
write_field = self.env["ir.model.fields"].search(
|
||||
[
|
||||
("model_id", "=", self.users_model.id),
|
||||
("name", "=", "write_date"),
|
||||
]
|
||||
)
|
||||
custom = self.env["custom.list.view"].create(
|
||||
{
|
||||
"name": "Test View Mod",
|
||||
"model_id": self.users_model.id,
|
||||
"list_view_id": self.view_users_tree.id,
|
||||
}
|
||||
)
|
||||
self.env["custom.list.view.line"].create(
|
||||
[
|
||||
{
|
||||
"custom_list_view_id": custom.id,
|
||||
"field_id": date_field.id,
|
||||
"after": login_field.id,
|
||||
"label": "Custom Label 1",
|
||||
},
|
||||
{
|
||||
"custom_list_view_id": custom.id,
|
||||
"field_id": date_field.id,
|
||||
"before": write_field.id,
|
||||
"label": "Custom Label 2",
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
# New field should not be in the view before button_apply_changes is clicked
|
||||
users_form = self.view_users_tree.arch
|
||||
self.assertNotIn(
|
||||
"create_date", users_form, msg="create_date should not be in the view."
|
||||
)
|
||||
|
||||
# Apply changes. Now new field should be there
|
||||
custom.button_apply_changes()
|
||||
users_form = self.view_users_tree.arch
|
||||
self.assertIn(
|
||||
"create_date", users_form, msg="create_date should be in the view."
|
||||
)
|
||||
|
||||
# Try to roll back. New field should not be in the view.
|
||||
custom.button_roll_back()
|
||||
users_form = self.view_users_tree.arch
|
||||
self.assertNotIn(
|
||||
"create_date", users_form, msg="Roll back feature does not work."
|
||||
)
|
||||
|
||||
# Trigger _compute_fields_domain with read(). No domain there.
|
||||
custom.list_view_id = new_view
|
||||
fields_domain = custom.line_ids[0].read(["fields_domain"])
|
||||
self.assertIsNotNone(fields_domain, msg="fields_domain should be empty.")
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_custom_list_view_form" model="ir.ui.view">
|
||||
<field name="name">custom.list.view.form.view</field>
|
||||
<field name="model">custom.list.view</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Customized List View">
|
||||
<header>
|
||||
<button
|
||||
name="button_apply_changes"
|
||||
type="object"
|
||||
string="Apply"
|
||||
groups="base.group_system"
|
||||
class="btn-primary"
|
||||
/>
|
||||
<button
|
||||
name="button_roll_back"
|
||||
type="object"
|
||||
string="Restore Original"
|
||||
groups="base.group_system"
|
||||
class="btn-secondary"
|
||||
/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name" />
|
||||
<field name="model_id" />
|
||||
<field name="model_name" />
|
||||
<field
|
||||
name="list_view_id"
|
||||
domain="[('model', '=', model_name), ('type', 'in', ['tree', 'list']), ('mode', '=', 'primary')]"
|
||||
readonly="model_name == False"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="line_ids">
|
||||
<list editable="top">
|
||||
<field name="model_id" invisible="1" />
|
||||
<field name="custom_list_view_id" invisible="1" />
|
||||
<field name="model_name" />
|
||||
<field
|
||||
name="field_id"
|
||||
domain="[('model_id', '=', model_id)]"
|
||||
/>
|
||||
<field name="label" />
|
||||
<field name="use_widget" />
|
||||
<field name="optional" />
|
||||
<field name="fields_domain" invisible="1" />
|
||||
<field
|
||||
name="before"
|
||||
required="after == False"
|
||||
readonly="after != False"
|
||||
domain="[('model_id', '=', model_id)]"
|
||||
/>
|
||||
<field
|
||||
name="after"
|
||||
domain="[('model_id', '=', model_id)]"
|
||||
required="before == False"
|
||||
readonly="before != False"
|
||||
/>
|
||||
</list>
|
||||
</field>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_custom_list_view_tree" model="ir.ui.view">
|
||||
<field name="name">custom.list.view.tree.view</field>
|
||||
<field name="model">custom.list.view</field>
|
||||
<field name="arch" type="xml">
|
||||
<list>
|
||||
<field name="model_id" />
|
||||
<field name="list_view_id" />
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_custom_list_view_tree" model="ir.actions.act_window">
|
||||
<field name="name">Customized List Views</field>
|
||||
<field name="res_model">custom.list.view</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="target">current</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_custom_list_view_tree"
|
||||
name="Customized List Views"
|
||||
action="action_custom_list_view_tree"
|
||||
parent="base.menu_administration"
|
||||
sequence="20"
|
||||
/>
|
||||
|
||||
</odoo>
|
||||