Project Maps, Trigger usw
This commit is contained in:
parent
13f556bf62
commit
3626b9c8c6
|
|
@ -0,0 +1,205 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<template xml:space="preserve">
|
||||
<t t-name="PresenceIndicator">
|
||||
<div id="oe_hr_attendance_status" class="fa fa-circle me-1 text-400 " role="img" aria-label="Available" title="Available">
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceCardLayout">
|
||||
<div class="o_hr_attendance_kiosk_mode_container o_home_menu_background d-flex flex-column align-items-center justify-content-center h-100 text-center">
|
||||
<span class="o_hr_attendance_kiosk_backdrop position-absolute top-0 start-0 end-0 bottom-0 bg-black-25"/>
|
||||
<div class="o_hr_attendance_clock bg-black-50 p-3 py-md-2 m-0 mt-md-5 me-md-5 h2 text-white font-monospace"/>
|
||||
<div t-attf-class="o_hr_attendance_kiosk_mode flex-grow-1 flex-md-grow-0 card pb-3 px-0 px-lg-5 {{kioskModeClasses ? kioskModeClasses : '' }}">
|
||||
<div class="card-body d-flex flex-column p-0 p-md-4">
|
||||
<t t-out="bodyContent"></t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceUserBadge">
|
||||
<div class="o_hr_attendance_user_badge o_home_menu_background d-flex align-items-end justify-content-center flex-grow-1 pt-5 pt-md-4 bg-odoo">
|
||||
<img class="img rounded-circle mb-n5" t-attf-src="/web/image?model=hr.employee.public&field=avatar_128&id=#{userId}" t-att-title="userName" height="80" t-att-alt="userName"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceCheckInOutButtons">
|
||||
<div class="flex-grow-1">
|
||||
<button t-attf-class="o_hr_attendance_sign_in_out_icon btn btn-{{ checked_in ? 'warning' : 'success' }} align-self-center px-5 py-3 mt-4 mb-2">
|
||||
<span class="align-middle fs-2 me-3 text-white" t-if="!checked_in">Check IN</span>
|
||||
<i t-attf-class="fa fa-4x fa-sign-{{ checked_in ? 'out' : 'in' }} align-middle"/>
|
||||
<span class="align-middle fs-2 ms-3" t-if="checked_in">Check OUT</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceKioskMode">
|
||||
<t t-call="HrAttendanceCardLayout">
|
||||
<t t-set="kioskModeClasses" t-translation="off">o_barcode_main pt-5</t>
|
||||
<t t-set="bodyContent">
|
||||
<h2 class="mb-2"><small>Welcome to</small> <t t-esc="widget.company_name"/></h2>
|
||||
<img t-attf-src="{{widget.company_image_url}}" alt="Company Logo" class="o_hr_attendance_kiosk_company_image align-self-center img img-fluid mb-3" width="200"/>
|
||||
<div class="o_hr_attendance_kiosk_welcome_row d-flex flex-column pb-5">
|
||||
<div class="col-md-5 mt-5 mb-5 mb-md-0 align-self-center" t-if="widget.kiosk_mode != 'manual'">
|
||||
<img src="/barcodes/static/img/barcode.png" alt="Barcode" style="width: 115px;height: 60px"/>
|
||||
<h6 class="mt-2 text-muted">Scan your badge</h6>
|
||||
</div>
|
||||
<div class="mt-5 align-self-end" t-if="widget.kiosk_mode == 'barcode_manual'">
|
||||
<button class="o_hr_attendance_button_employees btn btn-link">
|
||||
Identify Manually
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-5 align-self-center" t-if="widget.kiosk_mode == 'manual'">
|
||||
<button class="o_hr_attendance_button_employees btn btn-primary px-5 py-3 mt-4 mb-2">
|
||||
<span class="fs-2">Identify Manually</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceMyMainMenu">
|
||||
<t t-call="HrAttendanceCardLayout">
|
||||
<t t-set="bodyContent">
|
||||
<t t-if="widget.employee">
|
||||
<t t-set="checked_in" t-value="widget.employee.attendance_state=='checked_in'"/>
|
||||
|
||||
<t t-call="HrAttendanceUserBadge">
|
||||
<t t-set="userId" t-value="widget.employee.id"/>
|
||||
<t t-set="userName" t-value="widget.employee.name"/>
|
||||
</t>
|
||||
|
||||
<div class="flex-grow-1">
|
||||
<h1 class="mt-5" t-esc="widget.employee.name"/>
|
||||
<h3><t t-if="!checked_in">Welcome!</t><t t-else="">Want to check out?</t></h3>
|
||||
<h4 class="mt0 mb0 text-muted" t-if="checked_in">Today's work hours: <span t-esc="widget.hours_today"/></h4>
|
||||
</div>
|
||||
|
||||
<t t-call="HrAttendanceCheckInOutButtons"/>
|
||||
</t>
|
||||
<div class="alert alert-warning" t-else="">
|
||||
<b>Warning</b> : Your user should be linked to an employee to use attendance.<br/> Please contact your administrator.
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceKioskConfirm">
|
||||
<t t-call="HrAttendanceCardLayout">
|
||||
<t t-set="bodyContent">
|
||||
<t t-set="checked_in" t-value="widget.employee_state=='checked_in'"/>
|
||||
|
||||
<button class="o_hr_attendance_back_button btn btn-block btn-secondary btn-lg d-block d-md-none py-5">
|
||||
<i class="fa fa-chevron-left me-2"/> Go back
|
||||
</button>
|
||||
|
||||
<t t-if="widget.employee_id" t-call="HrAttendanceUserBadge">
|
||||
<t t-set="userId" t-value="widget.employee_id"/>
|
||||
<t t-set="userName" t-value="widget.employee_name"/>
|
||||
</t>
|
||||
|
||||
<button class="o_hr_attendance_back_button o_hr_attendance_back_button_md btn btn-secondary d-none d-md-inline-flex align-items-center position-absolute top-0 start-0 rounded-circle">
|
||||
<i class="fa fa-2x fa-fw fa-chevron-left me-1" role="img" aria-label="Go back" title="Go back"/>
|
||||
</button>
|
||||
|
||||
<div t-if="widget.employee_id" class="flex-grow-1">
|
||||
<h1 class="mt-5 mb8"><t t-esc="widget.employee_name"/></h1>
|
||||
<h3 class="mt8 mb24"><t t-if="!checked_in">Welcome!</t><t t-else="">Want to check out?</t></h3>
|
||||
<h4 class="mt0 mb0 text-muted" t-if="checked_in">Today's work hours: <span t-esc="widget.employee_hours_today"/></h4>
|
||||
|
||||
<t t-if="!widget.use_pin" t-call="HrAttendanceCheckInOutButtons"/>
|
||||
|
||||
<t t-else="">
|
||||
<h3 class="mt-4 mb0 text-muted">Please enter your PIN to <b t-if="checked_in">check out</b><b t-else="">check in</b></h3>
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2 o_hr_attendance_pin_pad">
|
||||
<div class="row g-0" >
|
||||
<div class="col-12 mb8 mt8">
|
||||
<input class="o_hr_attendance_PINbox border-0 bg-white fs-1 text-center" type="password" disabled="true"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-0">
|
||||
<t t-foreach="['1', '2', '3', '4', '5', '6', '7', '8', '9', ['C', 'btn-warning'], '0', ['ok', 'btn-primary']]" t-as="btn_name">
|
||||
<div class="col-4 p-1">
|
||||
<a href="#" t-attf-class="o_hr_attendance_PINbox_button btn {{btn_name[1]? btn_name[1] : 'btn-secondary border'}} btn-block btn-lg {{ 'o_hr_attendance_pin_pad_button_' + btn_name[0] }} d-flex align-items-center justify-content-center">
|
||||
<t t-esc="btn_name[0]"/>
|
||||
</a>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div t-else="" class="alert alert-danger mx-3" role="alert">
|
||||
<h4 class="alert-heading">Error: could not find corresponding employee.</h4>
|
||||
<p>Please return to the main menu.</p>
|
||||
</div>
|
||||
<a role="button" class="oe_attendance_sign_in_out" aria-label="Sign out" title="Sign out"/>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="HrAttendanceGreetingMessage">
|
||||
<t t-call="HrAttendanceCardLayout">
|
||||
<t t-set="bodyContent">
|
||||
<t t-set="checked_in" t-value="widget.employee_state=='checked_in'"/>
|
||||
|
||||
<t t-if="widget.attendance">
|
||||
<t t-call="HrAttendanceUserBadge">
|
||||
<t t-set="userId" t-value="widget.attendance.employee_id[0]"/>
|
||||
<t t-set="userName" t-value="widget.employee_name"/>
|
||||
</t>
|
||||
|
||||
<div t-if="widget.attendance.check_out" class="flex-grow-1">
|
||||
<h1 class="mt-5">Goodbye <t t-esc="widget.employee_name"/>!</h1>
|
||||
<h2 class="o_hr_attendance_message_message mt4 mb24"/>
|
||||
<div class="alert alert-info fs-2 mx-3" role="status">
|
||||
Checked out at <b><t t-esc="widget.attendance.check_out_time"/></b>
|
||||
<br/><b><t t-esc="widget.hours_today"/></b>
|
||||
</div>
|
||||
<div t-att-class="'alert ' + (widget.today_overtime_float >= 0 ? 'alert-success' : 'alert-danger') + ' h3 mx-3'" role="status">
|
||||
Extra hours today:
|
||||
<span t-esc="widget.today_overtime"/>
|
||||
</div>
|
||||
<t t-if="widget.total_overtime_float > 0">
|
||||
Total extra hours: <span t-esc="widget.total_overtime"/>
|
||||
</t>
|
||||
<h3 class="o_hr_attendance_random_message fst-italic mb24"/>
|
||||
<div class="o_hr_attendance_warning_message mt24 alert alert-warning" style="display:none" role="alert"/>
|
||||
</div>
|
||||
<div t-else="" class="flex-grow-1">
|
||||
<h1 class="mt-5 mb0">Welcome <t t-esc="widget.employee_name"/>!</h1>
|
||||
<h2 class="o_hr_attendance_message_message mt4 mb24"/>
|
||||
<div class="alert alert-info fs-2 mx-3" role="status">
|
||||
Checked in at <b><t t-esc="widget.attendance.check_in_time"/></b>
|
||||
</div>
|
||||
<h3 class="o_hr_attendance_random_message mb24"/>
|
||||
<div class="o_hr_attendance_warning_message mt24 alert alert-warning" style="display:none" role="alert"/>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<button class="o_hr_attendance_button_dismiss align-self-center btn btn-primary btn-lg px-5 py-3">
|
||||
<span class="fs-2" t-if="widget.attendance.check_out">Goodbye</span>
|
||||
<span class="fs-2" t-else="">OK</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="flex-grow-1">
|
||||
<div class="alert alert-warning mt-5 mx-3" role="alert">
|
||||
<h4 class="alert-heading">Invalid request</h4>
|
||||
<p>Please return to the main menu.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<button class="o_hr_attendance_button_dismiss btn btn-primary btn-lg fs-2 px-5 py-3">
|
||||
<i class="fa fa-chevron-left me-2"/>
|
||||
<span class="fs-2">Go back</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import main
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import http
|
||||
import logging
|
||||
from odoo.http import request
|
||||
_logger = logging.getLogger(__name__)
|
||||
from odoo.tools.json import scriptsafe
|
||||
|
||||
|
||||
class GoogleMap(http.Controller):
|
||||
'''
|
||||
This class generates on-the-fly partner maps that can be reused in every
|
||||
website page. To do so, just use an ``<iframe ...>`` whose ``src``
|
||||
attribute points to ``/google_map`` (this controller generates a complete
|
||||
HTML5 page).
|
||||
|
||||
URL query parameters:
|
||||
- ``partner_ids``: a comma-separated list of ids (partners to be shown)
|
||||
- ``partner_url``: the base-url to display the partner
|
||||
(eg: if ``partner_url`` is ``/partners/``, when the user will click on
|
||||
a partner on the map, it will be redirected to <myodoo>.com/partners/<id>)
|
||||
|
||||
In order to resize the map, simply resize the ``iframe`` with CSS
|
||||
directives ``width`` and ``height``.
|
||||
'''
|
||||
|
||||
@http.route(['/google_map'], type='http', auth="public", website=True, sitemap=False)
|
||||
def google_map(self, *arg, **post):
|
||||
projects = request.env['dss.projects'].sudo().search([('standort_visible', '=', True)])
|
||||
_logger.info("Google Maps " + str(projects)+ " and Record : "+str(len(projects)))
|
||||
projects_data = {
|
||||
"counter": len(projects),
|
||||
"projects": []
|
||||
}
|
||||
for project in projects.with_context(show_address=True):
|
||||
projects_data["projects"].append({
|
||||
'id': project.id,
|
||||
'name': project.projektname,
|
||||
'latitude': str(project.standort_lati) if project.standort_lati else False,
|
||||
'longitude': str(project.standort_long) if project.standort_long else False,
|
||||
})
|
||||
if 'customers' in post.get('partner_url', ''):
|
||||
partner_url = '/customers/'
|
||||
else:
|
||||
partner_url = '/partners/'
|
||||
|
||||
google_maps_api_key = request.website.google_maps_api_key
|
||||
values = {
|
||||
'partner_url': partner_url,
|
||||
'partner_data': scriptsafe.dumps(projects_data),
|
||||
'google_maps_api_key': google_maps_api_key,
|
||||
}
|
||||
return request.render("website_google_map.google_map", values)
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- views -->
|
||||
|
||||
<record id="view_attendance_tree" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.tree</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Employee attendances" editable="bottom" sample="1">
|
||||
<field name="employee_id"/>
|
||||
<field name="check_in"/>
|
||||
<field name="check_out"/>
|
||||
<field name="worked_hours" string="Work Hours" widget="float_time"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_hr_attendance_kanban" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.kanban</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_mobile" sample="1">
|
||||
<field name="employee_id"/>
|
||||
<field name="check_in"/>
|
||||
<field name="check_out"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div>
|
||||
<img t-att-src="kanban_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" class="oe_kanban_avatar o_image_24_cover mr4"/>
|
||||
<span class="o_kanban_record_title">
|
||||
<strong><t t-esc="record.employee_id.value"/></strong>
|
||||
</span>
|
||||
</div>
|
||||
<hr class="mt4 mb8"/>
|
||||
<div class="o_kanban_record_subtitle">
|
||||
<i class="fa fa-calendar" aria-label="Period" role="img" title="Period"></i>
|
||||
<t t-esc="record.check_in.value"/>
|
||||
- <t t-esc="record.check_out.value"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_view_form" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.form</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Employee attendances">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="employee_id"/>
|
||||
<field name="check_in"/>
|
||||
<field name="check_out"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_view_filter" model="ir.ui.view">
|
||||
<field name="name">hr_attendance_view_filter</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Hr Attendance Search">
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id" operator="child_of"/>
|
||||
<filter string="My Attendances" name="myattendances" domain="[('employee_id.user_id', '=', uid)]" />
|
||||
<separator/>
|
||||
<filter string="Check In" name="check_in_filter" date="check_in" default_period="last_month"/>
|
||||
<filter string="No Check Out" name="nocheckout" domain="[('check_out', '=', False)]" />
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Employee" name="employee" context="{'group_by': 'employee_id'}"/>
|
||||
<filter string="Check In" name="groupby_name" context="{'group_by': 'check_in'}"/>
|
||||
<filter string="Check Out" name="groupby_check_out" context="{'group_by': 'check_out'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- actions -->
|
||||
|
||||
<record id="hr_attendance_action" model="ir.actions.act_window">
|
||||
<field name="name">Attendances</field>
|
||||
<field name="res_model">hr.attendance</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="context">{"search_default_today":1}</field>
|
||||
<field name="search_view_id" ref="hr_attendance_view_filter" />
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_empty_folder">
|
||||
No attendance records found
|
||||
</p><p>
|
||||
The attendance records of your employees will be displayed here.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_action_employee" model="ir.actions.act_window">
|
||||
<field name="name">Attendances</field>
|
||||
<field name="res_model">hr.attendance</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'create': False}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_empty_folder">
|
||||
No attendance records to display
|
||||
</p><p>
|
||||
The attendance records of your employees will be displayed here.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_action_overview" model="ir.actions.act_window">
|
||||
<field name="name">Attendances</field>
|
||||
<field name="res_model">hr.attendance</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="context">{'create': False}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_empty_folder">
|
||||
No attendance records to display
|
||||
</p><p>
|
||||
Your attendance records will be displayed here.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_action_kiosk_mode" model="ir.actions.client">
|
||||
<field name="name">Attendances</field>
|
||||
<field name="tag">hr_attendance_kiosk_mode</field>
|
||||
<field name="target">fullscreen</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_action_my_attendances" model="ir.actions.client">
|
||||
<field name="name">Attendance</field>
|
||||
<field name="tag">hr_attendance_my_attendances</field>
|
||||
<field name="target">main</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_attendance_action_greeting_message" model="ir.actions.client">
|
||||
<field name="name">Message</field>
|
||||
<field name="tag">hr_attendance_greeting_message</field>
|
||||
</record>
|
||||
|
||||
<!-- Menus -->
|
||||
|
||||
<menuitem id="menu_hr_attendance_root" name="Attendances" sequence="205" groups="hr_attendance.group_hr_attendance,hr_attendance.group_hr_attendance_kiosk" web_icon="hr_attendance,static/description/icon.svg"/>
|
||||
|
||||
<menuitem id="menu_hr_attendance_my_attendances" name="Check In / Check Out" parent="menu_hr_attendance_root" sequence="1" groups="hr_attendance.group_hr_attendance" action="hr_attendance_action_my_attendances"/>
|
||||
|
||||
<menuitem id="menu_hr_attendance_attendances_overview" name="Attendances" parent="menu_hr_attendance_root" sequence="1" groups="hr_attendance.group_hr_attendance" action="hr_attendance_action_overview"/>
|
||||
|
||||
<menuitem id="menu_hr_attendance_kiosk_no_user_mode" name="Kiosk Mode" parent="menu_hr_attendance_root" sequence="10" groups="hr_attendance.group_hr_attendance_kiosk" action="hr_attendance_action_kiosk_mode"/>
|
||||
|
||||
<menuitem id="menu_hr_attendance_view_attendances" name="Attendances" parent="menu_hr_attendance_root" sequence="10" groups="hr_attendance.group_hr_attendance_user" action="hr_attendance_action"/>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
odoo.define('hr_attendance.my_attendances', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var core = require('web.core');
|
||||
var field_utils = require('web.field_utils');
|
||||
|
||||
const session = require('web.session');
|
||||
|
||||
var MyAttendances = AbstractAction.extend({
|
||||
contentTemplate: 'HrAttendanceMyMainMenu',
|
||||
events: {
|
||||
"click .o_hr_attendance_sign_in_out_icon": _.debounce(function() {
|
||||
this.update_attendance();
|
||||
}, 200, true),
|
||||
},
|
||||
|
||||
willStart: function () {
|
||||
var self = this;
|
||||
|
||||
var def = this._rpc({
|
||||
model: 'hr.employee',
|
||||
method: 'search_read',
|
||||
args: [[['user_id', '=', this.getSession().uid]], ['attendance_state', 'name', 'hours_today']],
|
||||
context: session.user_context,
|
||||
})
|
||||
.then(function (res) {
|
||||
self.employee = res.length && res[0];
|
||||
if (res.length) {
|
||||
self.hours_today = field_utils.format.float_time(self.employee.hours_today);
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([def, this._super.apply(this, arguments)]);
|
||||
},
|
||||
|
||||
update_attendance: function () {
|
||||
var self = this;
|
||||
this._rpc({
|
||||
model: 'hr.employee',
|
||||
method: 'attendance_manual',
|
||||
args: [[self.employee.id], 'hr_attendance.hr_attendance_action_my_attendances'],
|
||||
context: session.user_context,
|
||||
})
|
||||
.then(function(result) {
|
||||
if (result.action) {
|
||||
self.do_action(result.action);
|
||||
} else if (result.warning) {
|
||||
self.displayNotification({ title: result.warning, type: 'danger' });
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
core.action_registry.add('hr_attendance_my_attendances', MyAttendances);
|
||||
|
||||
return MyAttendances;
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import config
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = "res.partner"
|
||||
|
||||
date_localization = fields.Date(string='Geolocation Date')
|
||||
|
||||
def write(self, vals):
|
||||
# Reset latitude/longitude in case we modify the address without
|
||||
# updating the related geolocation fields
|
||||
if any(field in vals for field in ['street', 'zip', 'city', 'state_id', 'country_id']) \
|
||||
and not all('partner_%s' % field in vals for field in ['latitude', 'longitude']):
|
||||
vals.update({
|
||||
'partner_latitude': 0.0,
|
||||
'partner_longitude': 0.0,
|
||||
})
|
||||
return super().write(vals)
|
||||
|
||||
@api.model
|
||||
def _geo_localize(self, street='', zip='', city='', state='', country=''):
|
||||
geo_obj = self.env['base.geocoder']
|
||||
search = geo_obj.geo_query_address(street=street, zip=zip, city=city, state=state, country=country)
|
||||
result = geo_obj.geo_find(search, force_country=country)
|
||||
if result is None:
|
||||
search = geo_obj.geo_query_address(city=city, state=state, country=country)
|
||||
result = geo_obj.geo_find(search, force_country=country)
|
||||
return result
|
||||
|
||||
def geo_localize(self):
|
||||
# We need country names in English below
|
||||
if not self._context.get('force_geo_localize') \
|
||||
and (self._context.get('import_file') \
|
||||
or any(config[key] for key in ['test_enable', 'test_file', 'init', 'update'])):
|
||||
return False
|
||||
partners_not_geo_localized = self.env['res.partner']
|
||||
for partner in self.with_context(lang='en_US'):
|
||||
result = self._geo_localize(partner.street,
|
||||
partner.zip,
|
||||
partner.city,
|
||||
partner.state_id.name,
|
||||
partner.country_id.name)
|
||||
|
||||
if result:
|
||||
partner.write({
|
||||
'partner_latitude': result[0],
|
||||
'partner_longitude': result[1],
|
||||
'date_localization': fields.Date.context_today(partner)
|
||||
})
|
||||
else:
|
||||
partners_not_geo_localized |= partner
|
||||
if partners_not_geo_localized:
|
||||
self.env['bus.bus']._sendone(self.env.user.partner_id, 'simple_notification', {
|
||||
'title': _("Warning"),
|
||||
'message': _('No match found for %(partner_names)s address(es).', partner_names=', '.join(partners_not_geo_localized.mapped('name')))
|
||||
})
|
||||
return True
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.9 KiB |
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="google_map">
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||
<title>World Map</title>
|
||||
<link rel="stylesheet" type="text/css" href="/website_google_map/static/src/css/website_google_map.css"/>
|
||||
</head>
|
||||
<body t-att-data-partner-url="partner_url or None">
|
||||
<script>
|
||||
var odoo_partner_data = <t t-out="partner_data"/>;
|
||||
</script>
|
||||
<div id="odoo-google-map"></div>
|
||||
<t t-if="google_maps_api_key">
|
||||
<script t-attf-src="//maps.google.com/maps/api/js?key=#{google_maps_api_key}"></script>
|
||||
</t>
|
||||
<t t-else="1">
|
||||
<script src="//maps.google.com/maps/api/js"></script>
|
||||
</t>
|
||||
<script type="text/javascript" src="/website_google_map/static/src/lib/markerclusterer.js"></script>
|
||||
<script type="text/javascript" src="/website_google_map/static/src/js/website_google_map.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</template>
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue