scrapper & follower
This commit is contained in:
parent
5d59bc82b2
commit
c143ee4560
14 changed files with 450 additions and 38 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
.aider*
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@
|
|||
'license': 'GPL-3',
|
||||
'author': 'Bemade',
|
||||
'website': 'https://www.bemade.org',
|
||||
'depends': [],
|
||||
'depends': ['base', 'contacts', 'sale_management', 'sale' , 'partner_multi_relation'],
|
||||
'data': [
|
||||
# No security
|
||||
'views/res_partner_views.xml',
|
||||
'data/res_partner_relation_type.xml'
|
||||
],
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
|
|
|
|||
106
bemade_odoo_partner_scrapper/data/demo.xml
Normal file
106
bemade_odoo_partner_scrapper/data/demo.xml
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Added partner categories and partners to this file, because it
|
||||
turned out to be a bad idea to rely on demo data in base module,
|
||||
that can change from release to release. Only dependency on
|
||||
countries remain. They are less likely to change/disappear.
|
||||
-->
|
||||
<!-- Partner relation types -->
|
||||
<record id="rel_type_assistant" model="res.partner.relation.type">
|
||||
<field name="name">Is assistant of</field>
|
||||
<field name="name_inverse">Has assistant</field>
|
||||
<field name="contact_type_left">p</field>
|
||||
<field name="contact_type_right">p</field>
|
||||
</record>
|
||||
<record id="rel_type_competitor" model="res.partner.relation.type">
|
||||
<field name="name">Is competitor of</field>
|
||||
<field name="name_inverse">Is competitor of</field>
|
||||
<field name="contact_type_left">c</field>
|
||||
<field name="contact_type_right">c</field>
|
||||
<field name="is_symmetric" eval="True" />
|
||||
</record>
|
||||
<record id="rel_type_has_worked_for" model="res.partner.relation.type">
|
||||
<field name="name">Has worked for</field>
|
||||
<field name="name_inverse">Has former employee</field>
|
||||
<field name="contact_type_left">p</field>
|
||||
<field name="contact_type_right">c</field>
|
||||
</record>
|
||||
<!-- Categories -->
|
||||
<record id="res_partner_category_pmr_0" model="res.partner.category">
|
||||
<field name="name">Washing Companies</field>
|
||||
</record>
|
||||
<record id="res_partner_category_pmr_4" model="res.partner.category">
|
||||
<field name="name">Washing Gold</field>
|
||||
<field name="parent_id" ref="res_partner_category_pmr_0" />
|
||||
</record>
|
||||
<record id="res_partner_category_pmr_5" model="res.partner.category">
|
||||
<field name="name">Washing Silver</field>
|
||||
<field name="parent_id" ref="res_partner_category_pmr_0" />
|
||||
</record>
|
||||
<record id="res_partner_category_pmr_11" model="res.partner.category">
|
||||
<field name="name">Washing Services</field>
|
||||
<field name="parent_id" ref="res_partner_category_pmr_0" />
|
||||
</record>
|
||||
<!-- Partners -->
|
||||
<record id="res_partner_pmr_great" model="res.partner">
|
||||
<field name="name">Great Washing Powder Company</field>
|
||||
<field
|
||||
name="category_id"
|
||||
eval="[(6, 0, [ref('res_partner_category_pmr_11'), ref('res_partner_category_pmr_4')])]"
|
||||
/>
|
||||
<field name="is_company">1</field>
|
||||
<field name="city">Le Bourget du Lac</field>
|
||||
<field name="zip">73377</field>
|
||||
<field name="phone">+33 4 49 23 44 54</field>
|
||||
<field name="country_id" ref="base.fr" />
|
||||
<field name="street">93, Press Avenue</field>
|
||||
<field name="email">great@yourcompany.example.com</field>
|
||||
<field name="website">http://www.great.com</field>
|
||||
</record>
|
||||
<record id="res_partner_pmr_best" model="res.partner">
|
||||
<field name="name">Best Washing Powder Company</field>
|
||||
<field
|
||||
name="category_id"
|
||||
eval="[(6, 0, [ref('res_partner_category_pmr_4'), ref('res_partner_category_pmr_11')])]"
|
||||
/>
|
||||
<field name="is_company">1</field>
|
||||
<field name="city">Champs sur Marne</field>
|
||||
<field name="zip">77420</field>
|
||||
<field name="country_id" ref="base.fr" />
|
||||
<field name="email">best@yourcompany.example.com</field>
|
||||
<field name="phone">+33 1 64 61 04 01</field>
|
||||
<field name="street">12 rue Albert Einstein</field>
|
||||
<field name="website">http://www.best.com/</field>
|
||||
</record>
|
||||
<record id="res_partner_pmr_super" model="res.partner">
|
||||
<field name="name">Super Washing Powder Company</field>
|
||||
<field name="category_id" eval="[(6,0,[ref('res_partner_category_pmr_5')])]" />
|
||||
<field name="is_company">1</field>
|
||||
<field name="street">3rd Floor, Room 3-C,</field>
|
||||
<field
|
||||
name="street2"
|
||||
>Carretera Panamericana, Km 1, Urb. Delgado Chalbaud</field>
|
||||
<field name="city">Caracas</field>
|
||||
<field name="zip">1090</field>
|
||||
<field name="email">super@yourcompany.example.com</field>
|
||||
<field name="phone">+58 212 681 0538</field>
|
||||
<field name="country_id" ref="base.ve" />
|
||||
<field name="website">super.com</field>
|
||||
</record>
|
||||
<!-- Relations -->
|
||||
<record id="rel_1" model="res.partner.relation">
|
||||
<field name="left_partner_id" ref="res_partner_pmr_great" />
|
||||
<field name="right_partner_id" ref="res_partner_pmr_super" />
|
||||
<field name="type_id" ref="rel_type_competitor" />
|
||||
</record>
|
||||
<record id="rel_2" model="res.partner.relation">
|
||||
<field name="left_partner_id" ref="res_partner_pmr_best" />
|
||||
<field name="right_partner_id" ref="res_partner_pmr_super" />
|
||||
<field name="type_id" ref="rel_type_competitor" />
|
||||
</record>
|
||||
<record id="rel_3" model="res.partner.relation">
|
||||
<field name="left_partner_id" ref="res_partner_pmr_great" />
|
||||
<field name="right_partner_id" ref="res_partner_pmr_best" />
|
||||
<field name="type_id" ref="rel_type_competitor" />
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- Added partner categories and partners to this file, because it
|
||||
turned out to be a bad idea to rely on demo data in base module,
|
||||
that can change from release to release. Only dependency on
|
||||
countries remain. They are less likely to change/disappear.
|
||||
-->
|
||||
<!-- Partner relation types -->
|
||||
<record id="rel_type_odoo_partner" model="res.partner.relation.type">
|
||||
<field name="name">Is Odoo Partner Of</field>
|
||||
<field name="name_inverse">Is Odoo Client Of</field>
|
||||
<field name="contact_type_left">c</field>
|
||||
<field name="contact_type_right">c</field>
|
||||
</record>
|
||||
|
||||
<!-- Categories -->
|
||||
<record id="res_partner_category_odoo_partner" model="res.partner.category">
|
||||
<field name="name">Odoo Partner</field>
|
||||
</record>
|
||||
<record id="res_partner_category_odoo_partner_learning" model="res.partner.category">
|
||||
<field name="name">Learning Partner</field>
|
||||
<field name="parent_id" ref="res_partner_category_odoo_partner" />
|
||||
</record>
|
||||
<record id="res_partner_category_odoo_partner_ready" model="res.partner.category">
|
||||
<field name="name">Ready Partner</field>
|
||||
<field name="parent_id" ref="res_partner_category_odoo_partner" />
|
||||
</record>
|
||||
<record id="res_partner_category_odoo_partner_silver" model="res.partner.category">
|
||||
<field name="name">Silver Partner</field>
|
||||
<field name="parent_id" ref="res_partner_category_odoo_partner" />
|
||||
</record>
|
||||
<record id="res_partner_category_odoo_partner_gold" model="res.partner.category">
|
||||
<field name="name">Gold Partner</field>
|
||||
<field name="parent_id" ref="res_partner_category_odoo_partner" />
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,16 +1,85 @@
|
|||
from odoo import models, fields, api, _
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
||||
is_odoo_partner = fields.Boolean(string="Is Odoo Partner", default=False)
|
||||
is_odoo_user = fields.Boolean(string="Is Odoo User", default=False)
|
||||
is_odoo_partner = fields.Boolean(string="Is Odoo Partner", default=False, tracking=True)
|
||||
is_odoo_user = fields.Boolean(string="Is Odoo User", default=False, tracking=True)
|
||||
odoo_name = fields.Char(string="Name on Odoo", tracking=True)
|
||||
odoo_id = fields.Char(string="ID on Odoo", tracking=True)
|
||||
odoo_url = fields.Char(string="Odoo Partner URL", tracking=True, index=True)
|
||||
|
||||
@api.model
|
||||
def get_odoo_partner(self):
|
||||
|
||||
def extract_partner_data(url):
|
||||
page = requests.get("https://www.odoo.com" + url)
|
||||
soup = BeautifulSoup(page.content, 'html.parser')
|
||||
|
||||
partner_data = {}
|
||||
|
||||
partner_data['name'] = soup.find(id="partner_name").text
|
||||
|
||||
partner_type = soup.find('h3', class_='col-lg-12 text-center text-muted')
|
||||
if partner_type and partner_type.span:
|
||||
partner_data['partner_type'] = partner_type.span.text.strip()
|
||||
|
||||
partner_data['odoo_name'] = soup.find(id="partner_name").text
|
||||
address_div = soup.find('span', itemprop='streetAddress')
|
||||
email_div = soup.find('span', itemprop='email')
|
||||
phone_div = soup.find('span', itemprop='telephone')
|
||||
website_div = soup.find('span', itemprop='website')
|
||||
state_data = None
|
||||
|
||||
if address_div is not None:
|
||||
# Extract address components
|
||||
address_parts = address_div.get_text(separator="\n").split("\n")
|
||||
partner_data['street'] = address_parts[0]
|
||||
city_state_postal = address_parts[len(address_parts) - 2].split(', ')
|
||||
|
||||
if city_state_postal != [',']:
|
||||
partner_data['city'] = city_state_postal[0]
|
||||
state_postal = city_state_postal[1].split(' ')
|
||||
if len(state_postal) == 3 and len(state_postal[2]) == 3:
|
||||
partner_data['zip'] = state_postal[1] + state_postal[2]
|
||||
state_data = state_postal[0]
|
||||
elif len(state_postal) == 2 and state_postal[1]:
|
||||
if len(state_postal[1]) == 6:
|
||||
partner_data['zip'] = state_postal[1]
|
||||
state_data = state_postal[0]
|
||||
if len(state_postal[1]) == 3:
|
||||
if len(state_postal[0]) == 2:
|
||||
partner_data['zip'] = state_postal[1]
|
||||
state_data = state_postal[0]
|
||||
else:
|
||||
partner_data['zip'] = state_postal[0] + state_postal[1]
|
||||
else:
|
||||
if len(state_postal[0]) == 6:
|
||||
partner_data['zip'] = state_postal[0]
|
||||
else:
|
||||
state_data = state_postal[0]
|
||||
|
||||
# partner_data['country'] = address_parts[len(address_parts) - 1]
|
||||
|
||||
partner_data['email'] = email_div.string if email_div and email_div.string else ''
|
||||
partner_data['phone'] = phone_div.string if phone_div and phone_div.string else ''
|
||||
partner_data['website'] = website_div.string if website_div and website_div.string else ''
|
||||
partner_data['is_odoo_partner'] = True
|
||||
partner_data['is_odoo_user'] = True
|
||||
|
||||
if state_data:
|
||||
print(state_data)
|
||||
country_id = self.env['res.country'].search([('code', '=', 'CA')]).id
|
||||
partner_data['state_id'] = self.env['res.country.state'].search([('code', '=', state_data),('country_id', '=', country_id)]).id
|
||||
|
||||
return partner_data
|
||||
|
||||
# Load the webpage at the given url
|
||||
page = requests.get("https://www.odoo.com/fr_FR/partners/country/canada-36")
|
||||
# Parse the HTML content
|
||||
|
|
@ -50,37 +119,90 @@ class ResPartner(models.Model):
|
|||
for partner_url in partners_url:
|
||||
page = requests.get("https://www.odoo.com" + partner_url)
|
||||
soup = BeautifulSoup(page.content, 'html.parser')
|
||||
partner_name = soup.find(id="partner_name")
|
||||
address_div = soup.find('span', itemprop='streetAddress')
|
||||
email_div = soup.find('span', itemprop='email')
|
||||
phone_div = soup.find('span', itemprop='telephone')
|
||||
website_div = soup.find('span', itemprop='website')
|
||||
if address_div is not None:
|
||||
# Extract address components
|
||||
address_parts = address_div.get_text(separator="\n").split("\n")
|
||||
street_address = address_parts[0]
|
||||
city_state_postal = address_parts[len(address_parts)-2].split(', ')
|
||||
city = city_state_postal[0]
|
||||
state_postal = city_state_postal[1].split(' ')
|
||||
state = state_postal[0]
|
||||
postal_code = state_postal[1] + state_postal[2]
|
||||
country = address_parts[len(address_parts)-1]
|
||||
if email_div and email_div.string:
|
||||
email = email_div.string
|
||||
if phone_div and phone_div.string:
|
||||
phone = phone_div.string
|
||||
if website_div and website_div.string:
|
||||
website = website_div.string
|
||||
|
||||
# self.env['res.partner'].create({
|
||||
# 'name': partner_name,
|
||||
# 'street': street_address,
|
||||
# 'city': city,
|
||||
# 'state_id': state,
|
||||
# 'zip': postal_code,
|
||||
# 'country_id': country,
|
||||
# 'email': email,
|
||||
# 'phone': phone,
|
||||
# 'website': website,
|
||||
# 'is_odoo_partner': True,
|
||||
# })
|
||||
print (partner_url)
|
||||
odoo_partner = extract_partner_data(partner_url)
|
||||
print (odoo_partner)
|
||||
|
||||
# partner_name = soup.find(id="partner_name").text
|
||||
# address_div = soup.find('span', itemprop='streetAddress')
|
||||
# email_div = soup.find('span', itemprop='email')
|
||||
# phone_div = soup.find('span', itemprop='telephone')
|
||||
# website_div = soup.find('span', itemprop='website')
|
||||
# if address_div is not None:
|
||||
# # Extract address components
|
||||
# address_parts = address_div.get_text(separator="\n").split("\n")
|
||||
# street_address = address_parts[0]
|
||||
# city_state_postal = address_parts[len(address_parts) - 2].split(', ')
|
||||
#
|
||||
# if city_state_postal != [',']:
|
||||
# city = city_state_postal[0]
|
||||
# state_postal = city_state_postal[1].split(' ')
|
||||
#
|
||||
# if len(state_postal) == 3 and len(state_postal[2]) == 3:
|
||||
# postal_code = state_postal[1] + state_postal[2]
|
||||
# state = state_postal[0]
|
||||
# elif len(state_postal) == 2 and state_postal[1]:
|
||||
# if len(state_postal[1]) == 6:
|
||||
# postal_code = state_postal[1]
|
||||
# state = state_postal[0]
|
||||
# if len(state_postal[1]) == 3:
|
||||
# if len(state_postal[0]) == 2:
|
||||
# postal_code = state_postal[1]
|
||||
# state = state_postal[0]
|
||||
# else:
|
||||
# postal_code = state_postal[0] + state_postal[1]
|
||||
# state = ''
|
||||
# else:
|
||||
# if len(state_postal[0]) == 6:
|
||||
# postal_code = state_postal[0]
|
||||
# state = ''
|
||||
# else:
|
||||
# postal_code = ''
|
||||
# state = state_postal[0]
|
||||
# else:
|
||||
# city = ''
|
||||
# state = ''
|
||||
# postal_code = ''
|
||||
# # state = state_postal[0]
|
||||
# # postal_code = state_postal[1] + state_postal[2]
|
||||
# country = address_parts[len(address_parts) - 1]
|
||||
# if email_div and email_div.string:
|
||||
# email = email_div.string
|
||||
# else:
|
||||
# email = ''
|
||||
# if phone_div and phone_div.string:
|
||||
# phone = phone_div.string
|
||||
# else:
|
||||
# phone = ''
|
||||
# if website_div and website_div.string:
|
||||
# website = website_div.string
|
||||
# else:
|
||||
# website = ''
|
||||
|
||||
_logger.info(f"Processing {odoo_partner['name']} with email {odoo_partner['email']} and phone {odoo_partner['phone']} and website {odoo_partner['website']}")
|
||||
|
||||
exist_odoo_name = self.env['res.partner'].search([('odoo_name', '=', odoo_partner['odoo_name'])])
|
||||
if not exist_odoo_name:
|
||||
exist_email = self.env['res.partner'].search([('email', '=', odoo_partner['email'])])
|
||||
if not exist_email:
|
||||
exist_phone = self.env['res.partner'].search([('phone', '=', odoo_partner['phone'])])
|
||||
if not exist_phone:
|
||||
exist_website = self.env['res.partner'].search([('website', '=', odoo_partner['website'])])
|
||||
|
||||
if not exist_website or odoo_partner['website'] == '':
|
||||
if odoo_partner['website'] == '':
|
||||
del odoo_partner['website']
|
||||
self.env['res.partner'].create(odoo_partner)
|
||||
# self.env['res.partner'].create({
|
||||
# 'name': partner_name,
|
||||
# 'street': street_address,
|
||||
# 'city': city,
|
||||
# 'state_id': state_id.id if state_id else False,
|
||||
# 'zip': postal_code,
|
||||
# 'country_id': country_id.id if country_id else False,
|
||||
# 'email': email,
|
||||
# 'phone': phone,
|
||||
# 'website': website,
|
||||
# 'is_odoo_partner': True,
|
||||
# })
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
import ListController from 'web.ListController';
|
||||
import ListView from 'web.ListView';
|
||||
import KanbanController from 'web.KanbanController';
|
||||
import KanbanView from 'web.KanbanView';
|
||||
const viewRegistry = require('web.view_registry');
|
||||
|
||||
const OdooScrapperListController = ListController.extend({
|
||||
|
|
@ -32,3 +34,38 @@ const OdooScrapperListView = ListView.extend({
|
|||
// key must match with the js_class attribute of the tree view you want to modify
|
||||
viewRegistry.add('res_partner_odoo_scrapper_tree', OdooScrapperListView);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const OdooScrapperKanbanController = KanbanController.extend({
|
||||
// buttons_template must match the t-name on the template for the button (static xml)
|
||||
buttons_template: 'odoo_scrapper.kanban_view_buttons',
|
||||
events: _.extend({}, KanbanController.prototype.events, {
|
||||
'click .o_button_get_partner': '_onGetPartnerClick',
|
||||
}),
|
||||
// This may need to be async function() if there needs to be an await this._rpc({ ... }); call to not reload early
|
||||
_onGetPartnerClick: function () {
|
||||
this._rpc({
|
||||
model: 'res.partner',
|
||||
method: 'get_odoo_partner',
|
||||
args: [],
|
||||
}).then(() => {
|
||||
this.reload();
|
||||
});
|
||||
// Couldn't test this for real, but it runs the action. May need a this.reload()
|
||||
},
|
||||
});
|
||||
|
||||
const OdooScrapperKanbanView = KanbanView.extend({
|
||||
config: _.extend({}, KanbanView.prototype.config, {
|
||||
Controller: OdooScrapperKanbanController,
|
||||
}),
|
||||
});
|
||||
|
||||
// key must match with the js_class attribute of the tree view you want to modify
|
||||
viewRegistry.add('res_partner_odoo_scrapper_kanban', OdooScrapperKanbanView);
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<templates>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-extend="ListView.buttons" t-name="odoo_scrapper.list_view_buttons"> <!-- t-name must match with js controller button template -->
|
||||
<t t-jquery="button.o_list_button_add" t-operation="after">
|
||||
<button type="button"
|
||||
class="btn btn-primary ml4 o_button_get_partner"
|
||||
title="Sync Aliases"> Get Odoo Partner </button>
|
||||
title="Get Odoo Partner"> Get Odoo Partner </button>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
<t t-extend="KanbanView.buttons" t-name="odoo_scrapper.kanban_view_buttons">
|
||||
<t t-jquery="button" t-operation="after">
|
||||
<button type="button"
|
||||
class="btn btn-primary ml4 o_button_get_partner"
|
||||
title="Get Odoo Partner"> Get Odoo Partner </button>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
|
||||
|
||||
</templates>
|
||||
|
|
@ -15,4 +15,27 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_partner_kanban_inherit" model="ir.ui.view">
|
||||
<field name="name">res.partner.kanban.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.res_partner_kanban_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//kanban" position="attributes">
|
||||
<attribute name="js_class">res_partner_odoo_scrapper_kanban</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_res_partner_odoo_search">
|
||||
<field name="name">res.partner.odoo.search</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_res_partner_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="type_company" position="after">
|
||||
<filter domain="[('is_odoo_partner','=','True')]" string="Odoo Partners" name="res_partner_odoo_partner_filter"/>
|
||||
<filter domain="[('is_odoo_user','=','True')]" string="Odoo Users" name="res_partner_odoo_user_filter"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
|
|
|||
1
bemade_time_off_follower/__init__.py
Normal file
1
bemade_time_off_follower/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import models
|
||||
22
bemade_time_off_follower/__manifest__.py
Normal file
22
bemade_time_off_follower/__manifest__.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
"name": "Time Off Alternative Follower",
|
||||
"version": "15.0.0.0.1",
|
||||
"category": "Extra Tools",
|
||||
'summary': 'Add Alternative Follower When Receiving Message While On Time Off',
|
||||
"description": """
|
||||
Add Alternative Follower When Receiving Message While On Time Off
|
||||
""",
|
||||
"author": "Bemade",
|
||||
'website': 'https://www.bemade.org',
|
||||
"depends": [
|
||||
'hr_holidays',
|
||||
'mail'
|
||||
],
|
||||
"data": [
|
||||
'views/hr_leave_views.xml',
|
||||
],
|
||||
"auto_install": False,
|
||||
"installable": True,
|
||||
'license': 'OPL-1'
|
||||
}
|
||||
2
bemade_time_off_follower/models/__init__.py
Normal file
2
bemade_time_off_follower/models/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
from . import hr_leave
|
||||
from . import mail_thread
|
||||
10
bemade_time_off_follower/models/hr_leave.py
Normal file
10
bemade_time_off_follower/models/hr_leave.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from odoo import models, fields, api
|
||||
|
||||
class HrLeave(models.Model):
|
||||
_inherit = 'hr.leave'
|
||||
|
||||
alternate_follower_id = fields.Many2one(
|
||||
comodel_name='res.users',
|
||||
string='Alternate Follower',
|
||||
help='User who will receive a copy of all communications related to this leave',
|
||||
)
|
||||
26
bemade_time_off_follower/models/mail_thread.py
Normal file
26
bemade_time_off_follower/models/mail_thread.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
from odoo import models, api
|
||||
|
||||
class MailThread(models.AbstractModel):
|
||||
_inherit = 'mail.thread'
|
||||
|
||||
@api.returns('mail.message', lambda value: value.id)
|
||||
def message_post(self, **kwargs):
|
||||
message = super(MailThread, self).message_post(**kwargs)
|
||||
|
||||
leave_env = self.env['hr.leave']
|
||||
for recipient in message.notification_ids.mapped('res_partner_id'):
|
||||
leave = leave_env.search([
|
||||
('state', '=', 'validate'),
|
||||
('date_from', '<=', fields.Date.today()),
|
||||
('date_to', '>=', fields.Date.today()),
|
||||
('employee_id.user_id.partner_id', '=', recipient.id),
|
||||
], limit=1)
|
||||
|
||||
if leave and leave.alternate_follower_id:
|
||||
message.write({
|
||||
'notification_ids': [(0, 0, {
|
||||
'res_partner_id': leave.alternate_follower_id.partner_id.id,
|
||||
# 'notification_type': 'email',
|
||||
})]
|
||||
})
|
||||
return message
|
||||
13
bemade_time_off_follower/views/hr_leave_views.xml
Normal file
13
bemade_time_off_follower/views/hr_leave_views.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="hr_leave_form_view_inherit" model="ir.ui.view">
|
||||
<field name="name">hr.leave.form.view.inherit</field>
|
||||
<field name="model">hr.leave</field>
|
||||
<field name="inherit_id" ref="hr_holidays.hr_leave_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='holiday_status_id']" position="after">
|
||||
<field name="alternate_follower_id"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in a new issue