Apply manager's feedback, complete vehicle changes, and fix security groups

This commit is contained in:
cerenX9
2026-07-23 12:59:18 +03:00
parent a1b09ad369
commit 12bc1e3f99
15 changed files with 535 additions and 179 deletions

View File

@@ -1,2 +1,12 @@
from . import models
from . import controllers
from . import models
def pre_init_hook(env):
env.cr.execute("""
DELETE FROM ir_model_data
WHERE module = 'mymach_fleet_intelligence'
AND name IN (
'group_mymach_fleet_user', 'group_mymach_fleet_manager',
'group_fleet_user_v2', 'group_fleet_manager_v2'
)
""")

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
{
'name': 'MyMach Akıllı Filo & Finansal Rota Motoru',
'version': '1.0',
'version': '19.0.1.1.0',
'summary': 'Standart Odoo filo modülünden bağımsız, otonom rota takibi, cihaz entegrasyonu ve finansal maliyet hesaplaması yapan gelişmiş filo modülü.',
'description': 'MyMach Fleet Intelligence - Faz 1 (Sözleşme Yönetimi)',
'author': 'MyMach',
@@ -10,6 +10,8 @@
'data': [
'security/security_groups.xml',
'security/ir.model.access.csv',
'security/record_rules.xml',
'views/res_users_views.xml',
'data/ir_cron.xml',
'data/cron_jobs.xml',
'views/contract_views.xml',
@@ -22,13 +24,15 @@
'views/dashboard_template.xml',
'views/fleet_fine_views.xml',
'views/fleet_handover_views.xml',
'views/home_views.xml',
'views/fuel_index_views.xml',
'views/home_views.xml',
'views/driver_tracker_template.xml',
],
'installable': True,
'application': True,
'auto_install': False,
'license': 'LGPL-3',
'pre_init_hook': 'pre_init_hook',
'assets': {
'web.assets_backend': [
'mymach_fleet_intelligence/static/src/css/aesthetics.css',

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from odoo import http, _
from odoo import http
from odoo.http import request
import json
@@ -12,9 +12,8 @@ class MymachFleetController(http.Controller):
Faz 8 (Zero-Trust): Sadece geçerli API_TOKEN ile veri kabul edilir. (Anti-Spoofing)
"""
api_token = kwargs.get('api_token')
# Token veritabanından çekiliyor (Güvenli yöntem)
valid_token = request.env['ir.config_parameter'].sudo().get_param('mymach.api_ping_token', default='MYMACH_SECURE_TOKEN_2026')
if api_token != valid_token:
# Gerçek senaryoda bu token config dosyasından veya veritabanından çekilir.
if api_token != 'MYMACH_SECURE_TOKEN_2026':
return {'status': 'error', 'message': 'Erişim Reddedildi! Geçersiz API Token (Zero-Trust Security).'}
vehicle = request.env['fleet.vehicle'].sudo().browse(vehicle_id)
@@ -57,10 +56,6 @@ class MymachFleetController(http.Controller):
"""
Canlı harita ve telemetri paneli için 5 test sürücüsünün rota ve sapma verilerini toplar.
"""
# Güvenlik (DAST) - Sadece Yöneticiler Bu Rotaya Erişebilir
if not request.env.user.has_group('mymach_fleet_intelligence.group_mymach_fleet_manager'):
return request.redirect('/web')
# 5 Farklı Sürücü İçin Gerçek Cadde/Sokak/Otoban Kıvrımlı Rota Verileri (Sürücü A - E)
routes_db = {
'34-AHM-101': {
@@ -250,22 +245,22 @@ class MymachFleetController(http.Controller):
# Uyumluluk ve İhlal Mantığı
if is_manager_approved:
is_violation = False
deviation_label = _("✅ Yönetici Tarafından Onaylı Rota")
deviation_label = "✅ Yönetici Tarafından Onaylı Rota"
elif is_over_1km and policy == 'forbidden':
is_violation = True
deviation_label = _("🚨 UYUMSUZ (%(dev)s km Yasaklı Şahsi Kullanım)") % {'dev': deviation}
deviation_label = f"🚨 UYUMSUZ ({deviation} km Yasaklı Şahsi Kullanım)"
elif is_over_1km and policy == 'limit' and contract and ((contract.personal_km_used + deviation) > (contract.max_km_limit or 0.0)):
is_violation = True
deviation_label = _("🚨 UYUMSUZ (%(dev)s km Sözleşme KM Sınırııldı)") % {'dev': deviation}
deviation_label = f"🚨 UYUMSUZ ({deviation} km Sözleşme KM Sınırııldı)"
elif has_free_use:
is_violation = False
deviation_label = _("Serbest İzinli Şahsi Kullanım")
deviation_label = "Serbest İzinli Şahsi Kullanım"
elif is_over_1km:
is_violation = True
deviation_label = _("🚨 UYUMSUZ (%(dev)s km Sapma - Yöneticiye Alarm)") % {'dev': deviation}
deviation_label = f"🚨 UYUMSUZ ({deviation} km Sapma - Yöneticiye Alarm)"
else:
is_violation = False
deviation_label = _("%(dev)s km Uyumlu Rota") % {'dev': deviation}
deviation_label = f"{deviation} km Uyumlu Rota"
# Eğer UYUMSUZ (İhlal) ise yöneticiye güvenlik bildirimi/logu üret
if is_violation and v.driver_id:
@@ -314,18 +309,7 @@ class MymachFleetController(http.Controller):
'green_route': rdata['green_route'],
'red_route': rdata['red_route'],
'alt1': rdata['alt1'],
'alt2': rdata['alt2'],
'label_home': _("Personel Ev Adresi:"),
'label_start': _("Başlangıç Noktası (İş Yeri):"),
'label_dest': _("Ulaşım Noktası (Hedef / Proje):"),
'label_plan': _("Planlanan / Kat Edilen:"),
'label_dev': _("Sapma & Durum:"),
'label_policy': _("Sözleşme Poliçesi:"),
'badge_violation': _("🚨 UYUMSUZ ROTA (YÖNETİCİYE ALARM BİLDİRİLDİ)"),
'badge_approved': _("✅ YÖNETİCİ ONAYLI PROJE GÖREVİ"),
'badge_ok': _("✅ UYUMLU ANA ROTA"),
'label_score': _('Skor:'),
'driver_tag_str': _(rdata['driver_tag']) if rdata['driver_tag'] else ''
'alt2': rdata['alt2']
})
# Son 50 güvenlik logunu çek (Haritada göstermek için)
@@ -350,3 +334,13 @@ class MymachFleetController(http.Controller):
'logs_json': json.dumps(log_list, ensure_ascii=True),
'google_maps_api_key': google_maps_api_key
})
@http.route('/mymach/driver/tracker', type='http', auth='user')
def driver_tracker(self, **kwargs):
"""
Sürücülerin mobil cihazlarından konum paylaşabilmeleri için web arayüzü.
"""
vehicles = request.env['fleet.vehicle'].sudo().search([])
return request.render('mymach_fleet_intelligence.driver_tracker_template', {
'vehicles': vehicles,
})

View File

@@ -10,3 +10,4 @@ from . import fleet_handover
from . import periodic_maintenance
from . import fuel_index
from . import res_currency
from . import res_users

View File

@@ -43,10 +43,10 @@ class FleetVehicle(models.Model):
cost_curr2_id = fields.Many2one('res.currency', string="Selected 3rd Currency (FX 2)", default=lambda self: self.env.ref('base.EUR', raise_if_not_found=False))
active_display_currency_id = fields.Many2one('res.currency', string="Active Display Currency", compute="_compute_financial_metrics")
cost_display_mode = fields.Selection([
('try', '🇹🇷 Ana Para Birimi (TL)'),
('try', '₺ Şirket Ana Para Birimi'),
('curr1', '💵 2. Para Birimi (Döviz 1)'),
('curr2', '💶 3. Para Birimi (Döviz 2)'),
('average', '📊 3 Para Birimli Ortalama Tahminî Değer (TL)')
('average', '📊 3 Para Birimli Ortalama Tahminî Değer')
], string="Currency Display Mode (Listbox)", default='average')
cost_per_km_avg_display = fields.Float(string="Average Estimated Value Per KM", compute="_compute_financial_metrics")
@@ -72,15 +72,40 @@ class FleetVehicle(models.Model):
# Yakıt Türü Seçenekleri Özelleştirme (Benzin / LPG Dahil)
fuel_type = fields.Selection([
('gasoline', 'Benzinli'),
('diesel', 'Dizel'),
('electric', 'Tam Elektrikli (EV)'),
('lpg', 'Benzin / LPG'),
('gasoline_lpg', 'Benzin / LPG'),
('hybrid_gasoline', 'Hibrit (Elektrik / Benzin)'),
('hybrid_diesel', 'Hibrit (Elektrik / Dizel)')
], string="Fuel Type", default='diesel')
fuel_type = fields.Selection(
selection_add=[
('gasoline_lpg', 'Benzin / LPG'),
('hybrid_gasoline', 'Hibrit (Elektrik / Benzin)'),
('hybrid_diesel', 'Hibrit (Elektrik / Dizel)')
],
ondelete={
'gasoline_lpg': 'set null',
'hybrid_gasoline': 'set null',
'hybrid_diesel': 'set null',
}
)
# Faz 2.3: Araç Sınıfı / Türü
mymach_vehicle_type = fields.Selection([
('sedan', 'Otomobil (Sedan / Hatchback / SUV)'),
('panelvan', 'Kamyonet (Panelvan / Kombivan)'),
('minibus', 'Minibüs'),
('midibus', 'Midibüs / Otobüs'),
('pickup', 'Pick-up'),
('truck', 'Kamyon'),
('tractor', 'Çekici (Tır)'),
('trailer', 'Dorse / Römork'),
('motorcycle', 'Motosiklet (A2 / A Sınıfı)'),
('scooter', 'Scooter / Moped (50cc altı / L1-L2-L6 Sınıfı)'),
('ebike', 'Elektrikli Bisiklet (E-Bike)'),
('escooter', 'Elektrikli Scooter (E-Scooter)'),
('bicycle', 'Bisiklet'),
('atv', 'ATV / UTV (Arazi Araçları)'),
('machine', 'İş Makinesi / Traktör')
], string="Araç Sınıfı / Türü", default='sedan')
# Faz 2.6: Şirket Lokasyonu
mymach_location_id = fields.Many2one('res.partner', string="Şirket Lokasyonu", domain="[('is_company', '=', True)]")
# Faz 8: Akaryakıt API ve Prediktif Bakım
tank_capacity = fields.Float(string="Fuel Tank Capacity (Liters)", default=50.0)
@@ -114,9 +139,7 @@ class FleetVehicle(models.Model):
costs = vehicle.maintenance_cost_ids
total_amount_try = sum(costs.mapped('amount')) if costs else 0.0
odo = vehicle.odometer or 1.0
if odo <= 0:
odo = 1.0
odo = vehicle.odometer or 0.0
vehicle.total_expense_amount = total_amount_try
vehicle.total_driven_odometer = odo

View File

@@ -14,32 +14,7 @@ class MymachFleetHome(models.Model):
display_title = fields.Char(compute='_compute_display_title')
upcoming_maintenance_html = fields.Html(compute='_compute_upcoming_maintenance_html')
# Dil Seçimi
dashboard_lang_id = fields.Many2one(
'res.lang',
string="Language",
compute='_compute_dashboard_lang',
inverse='_inverse_dashboard_lang',
domain=[('active', '=', True)]
)
@api.depends()
def _compute_dashboard_lang(self):
for record in self:
lang_code = self.env.user.lang or 'tr_TR'
lang = self.env['res.lang'].search([('code', '=', lang_code)], limit=1)
record.dashboard_lang_id = lang
def _inverse_dashboard_lang(self):
for record in self:
if record.dashboard_lang_id and self.env.user.lang != record.dashboard_lang_id.code:
self.env.user.lang = record.dashboard_lang_id.code
def action_apply_language(self):
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
# Dil Seçimi (Odoo Standart Dil Motoruna Bırakıldı)
# Grafik Filtreleri ve HTML Tuvali
selected_vehicle_id = fields.Many2one('fleet.vehicle', string="Selected Vehicle")

View File

@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class ResUsers(models.Model):
_inherit = 'res.users'
mymach_role = fields.Selection([
('none', 'Boş (Yetkisiz)'),
('user', 'Kullanıcı (Personel)'),
('manager', 'Yönetici (Admin)')
], string="Kullanıcı Yetkisi", default='none', help="MyMach Filo Sistemi Erişim Yetkisi")
@api.model_create_multi
def create(self, vals_list):
users = super(ResUsers, self).create(vals_list)
for user in users:
user._sync_mymach_groups()
return users
def write(self, vals):
res = super(ResUsers, self).write(vals)
if 'mymach_role' in vals:
for user in self:
user._sync_mymach_groups()
return res
def _sync_mymach_groups(self):
""" mymach_role alanındaki seçime göre standart grupları otomatik atar """
group_user = self.env.ref('mymach_fleet_intelligence.group_mymach_fleet_user', raise_if_not_found=False)
group_manager = self.env.ref('mymach_fleet_intelligence.group_mymach_fleet_manager', raise_if_not_found=False)
if not group_user or not group_manager:
return
# Önce ikisini de kaldır
self.groups_id = [(3, group_user.id), (3, group_manager.id)]
# Seçime göre yeniden ekle
if self.mymach_role == 'user':
self.groups_id = [(4, group_user.id)]
elif self.mymach_role == 'manager':
self.groups_id = [(4, group_manager.id), (4, group_user.id)]

View File

@@ -1,18 +1,18 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mymach_fleet_contract_user,contract_user,model_mymach_fleet_contract,group_mymach_fleet_user,1,0,0,0
access_mymach_fleet_contract_manager,contract_manager,model_mymach_fleet_contract,group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_maintenance_cost_manager,maintenance_cost_manager,model_mymach_fleet_maintenance_cost,group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_security_log_user,log_user,model_mymach_fleet_security_log,group_mymach_fleet_user,1,1,0,0
access_mymach_fleet_security_log_manager,log_manager,model_mymach_fleet_security_log,group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_route_wizard_user,route_wizard_user,model_mymach_fleet_route_exception_wizard,group_mymach_fleet_user,1,1,1,1
access_mymach_fleet_reservation_user,reservation_user,model_mymach_fleet_reservation,group_mymach_fleet_user,1,1,1,0
access_mymach_fleet_reservation_manager,reservation_manager,model_mymach_fleet_reservation,group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_home_user,home_user,model_mymach_fleet_home,group_mymach_fleet_user,1,1,1,1
access_mymach_fleet_fine_user,fine_user,model_mymach_fleet_fine,group_mymach_fleet_user,1,0,0,0
access_mymach_fleet_fine_manager,fine_manager,model_mymach_fleet_fine,group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_handover_user,handover_user,model_mymach_fleet_handover,group_mymach_fleet_user,1,1,1,0
access_mymach_fleet_handover_manager,handover_manager,model_mymach_fleet_handover,group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_periodic_maintenance_user,periodic_maintenance_user,model_mymach_fleet_periodic_maintenance,group_mymach_fleet_user,1,1,0,0
access_mymach_fleet_periodic_maintenance_manager,periodic_maintenance_manager,model_mymach_fleet_periodic_maintenance,group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_fuel_index_user,mymach.fleet.fuel.index.user,model_mymach_fleet_fuel_index,group_mymach_fleet_user,1,0,0,0
access_mymach_fleet_fuel_index_manager,mymach.fleet.fuel.index.manager,model_mymach_fleet_fuel_index,group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_contract_user,contract_user,model_mymach_fleet_contract,mymach_fleet_intelligence.group_mymach_fleet_user,1,0,0,0
access_mymach_fleet_contract_manager,contract_manager,model_mymach_fleet_contract,mymach_fleet_intelligence.group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_maintenance_cost_manager,maintenance_cost_manager,model_mymach_fleet_maintenance_cost,mymach_fleet_intelligence.group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_security_log_user,log_user,model_mymach_fleet_security_log,mymach_fleet_intelligence.group_mymach_fleet_user,1,1,0,0
access_mymach_fleet_security_log_manager,log_manager,model_mymach_fleet_security_log,mymach_fleet_intelligence.group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_route_wizard_user,route_wizard_user,model_mymach_fleet_route_exception_wizard,mymach_fleet_intelligence.group_mymach_fleet_user,1,1,1,1
access_mymach_fleet_reservation_user,reservation_user,model_mymach_fleet_reservation,mymach_fleet_intelligence.group_mymach_fleet_user,1,1,1,0
access_mymach_fleet_reservation_manager,reservation_manager,model_mymach_fleet_reservation,mymach_fleet_intelligence.group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_home_user,home_user,model_mymach_fleet_home,mymach_fleet_intelligence.group_mymach_fleet_user,1,1,1,1
access_mymach_fleet_fine_user,fine_user,model_mymach_fleet_fine,mymach_fleet_intelligence.group_mymach_fleet_user,1,0,0,0
access_mymach_fleet_fine_manager,fine_manager,model_mymach_fleet_fine,mymach_fleet_intelligence.group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_handover_user,handover_user,model_mymach_fleet_handover,mymach_fleet_intelligence.group_mymach_fleet_user,1,1,1,0
access_mymach_fleet_handover_manager,handover_manager,model_mymach_fleet_handover,mymach_fleet_intelligence.group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_periodic_maintenance_user,periodic_maintenance_user,model_mymach_fleet_periodic_maintenance,mymach_fleet_intelligence.group_mymach_fleet_user,1,1,0,0
access_mymach_fleet_periodic_maintenance_manager,periodic_maintenance_manager,model_mymach_fleet_periodic_maintenance,mymach_fleet_intelligence.group_mymach_fleet_manager,1,1,1,1
access_mymach_fleet_fuel_index_user,mymach.fleet.fuel.index.user,model_mymach_fleet_fuel_index,mymach_fleet_intelligence.group_mymach_fleet_user,1,0,0,0
access_mymach_fleet_fuel_index_manager,mymach.fleet.fuel.index.manager,model_mymach_fleet_fuel_index,mymach_fleet_intelligence.group_mymach_fleet_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_mymach_fleet_contract_user contract_user model_mymach_fleet_contract group_mymach_fleet_user mymach_fleet_intelligence.group_mymach_fleet_user 1 0 0 0
3 access_mymach_fleet_contract_manager contract_manager model_mymach_fleet_contract group_mymach_fleet_manager mymach_fleet_intelligence.group_mymach_fleet_manager 1 1 1 1
4 access_mymach_fleet_maintenance_cost_manager maintenance_cost_manager model_mymach_fleet_maintenance_cost group_mymach_fleet_manager mymach_fleet_intelligence.group_mymach_fleet_manager 1 1 1 1
5 access_mymach_fleet_security_log_user log_user model_mymach_fleet_security_log group_mymach_fleet_user mymach_fleet_intelligence.group_mymach_fleet_user 1 1 0 0
6 access_mymach_fleet_security_log_manager log_manager model_mymach_fleet_security_log group_mymach_fleet_manager mymach_fleet_intelligence.group_mymach_fleet_manager 1 1 1 1
7 access_mymach_fleet_route_wizard_user route_wizard_user model_mymach_fleet_route_exception_wizard group_mymach_fleet_user mymach_fleet_intelligence.group_mymach_fleet_user 1 1 1 1
8 access_mymach_fleet_reservation_user reservation_user model_mymach_fleet_reservation group_mymach_fleet_user mymach_fleet_intelligence.group_mymach_fleet_user 1 1 1 0
9 access_mymach_fleet_reservation_manager reservation_manager model_mymach_fleet_reservation group_mymach_fleet_manager mymach_fleet_intelligence.group_mymach_fleet_manager 1 1 1 1
10 access_mymach_fleet_home_user home_user model_mymach_fleet_home group_mymach_fleet_user mymach_fleet_intelligence.group_mymach_fleet_user 1 1 1 1
11 access_mymach_fleet_fine_user fine_user model_mymach_fleet_fine group_mymach_fleet_user mymach_fleet_intelligence.group_mymach_fleet_user 1 0 0 0
12 access_mymach_fleet_fine_manager fine_manager model_mymach_fleet_fine group_mymach_fleet_manager mymach_fleet_intelligence.group_mymach_fleet_manager 1 1 1 1
13 access_mymach_fleet_handover_user handover_user model_mymach_fleet_handover group_mymach_fleet_user mymach_fleet_intelligence.group_mymach_fleet_user 1 1 1 0
14 access_mymach_fleet_handover_manager handover_manager model_mymach_fleet_handover group_mymach_fleet_manager mymach_fleet_intelligence.group_mymach_fleet_manager 1 1 1 1
15 access_mymach_fleet_periodic_maintenance_user periodic_maintenance_user model_mymach_fleet_periodic_maintenance group_mymach_fleet_user mymach_fleet_intelligence.group_mymach_fleet_user 1 1 0 0
16 access_mymach_fleet_periodic_maintenance_manager periodic_maintenance_manager model_mymach_fleet_periodic_maintenance group_mymach_fleet_manager mymach_fleet_intelligence.group_mymach_fleet_manager 1 1 1 1
17 access_mymach_fleet_fuel_index_user mymach.fleet.fuel.index.user model_mymach_fleet_fuel_index group_mymach_fleet_user mymach_fleet_intelligence.group_mymach_fleet_user 1 0 0 0
18 access_mymach_fleet_fuel_index_manager mymach.fleet.fuel.index.manager model_mymach_fleet_fuel_index group_mymach_fleet_manager mymach_fleet_intelligence.group_mymach_fleet_manager 1 1 1 1

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<!-- RECORD RULES (Sözleşme) -->
<record id="rule_mymach_fleet_contract_user" model="ir.rule">
<field name="name">Sürücüler kendi sözleşmesini görür</field>
<field name="model_id" ref="model_mymach_fleet_contract"/>
<field name="domain_force">[('driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_contract_manager" model="ir.rule">
<field name="name">Yöneticiler tüm sözleşmeleri görür</field>
<field name="model_id" ref="model_mymach_fleet_contract"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Rezervasyon) -->
<record id="rule_mymach_fleet_reservation_user" model="ir.rule">
<field name="name">Sürücüler kendi taleplerini görür</field>
<field name="model_id" ref="model_mymach_fleet_reservation"/>
<field name="domain_force">[('create_uid', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_reservation_manager" model="ir.rule">
<field name="name">Yöneticiler tüm talepleri görür</field>
<field name="model_id" ref="model_mymach_fleet_reservation"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Teslimat / Handover) -->
<record id="rule_mymach_fleet_handover_user" model="ir.rule">
<field name="name">Sürücüler kendi teslimatlarını görür</field>
<field name="model_id" ref="model_mymach_fleet_handover"/>
<field name="domain_force">[('driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_handover_manager" model="ir.rule">
<field name="name">Yöneticiler tüm teslimatları görür</field>
<field name="model_id" ref="model_mymach_fleet_handover"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Güvenlik / Rota Sapma Logları) -->
<record id="rule_mymach_fleet_security_log_user" model="ir.rule">
<field name="name">Sürücüler kendi rota sapma uyarılarını görür</field>
<field name="model_id" ref="model_mymach_fleet_security_log"/>
<field name="domain_force">[('vehicle_id.driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_security_log_manager" model="ir.rule">
<field name="name">Yöneticiler tüm güvenlik ihlallerini görür</field>
<field name="model_id" ref="model_mymach_fleet_security_log"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Cezalar ve Giderler) -->
<record id="rule_mymach_fleet_fine_user" model="ir.rule">
<field name="name">Sürücüler kendi cezalarını görür</field>
<field name="model_id" ref="model_mymach_fleet_fine"/>
<field name="domain_force">[('driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_fine_manager" model="ir.rule">
<field name="name">Yöneticiler tüm cezaları görür</field>
<field name="model_id" ref="model_mymach_fleet_fine"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Periyodik Bakım) -->
<record id="rule_mymach_fleet_periodic_maintenance_user" model="ir.rule">
<field name="name">Sürücüler kendi araçlarının bakımlarını görür</field>
<field name="model_id" ref="model_mymach_fleet_periodic_maintenance"/>
<field name="domain_force">[('vehicle_id.driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_periodic_maintenance_manager" model="ir.rule">
<field name="name">Yöneticiler tüm bakımları görür</field>
<field name="model_id" ref="model_mymach_fleet_periodic_maintenance"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
</data>
</odoo>

View File

@@ -10,91 +10,7 @@
<!-- Yönetici (Admin) Grubu -->
<record id="group_mymach_fleet_manager" model="res.groups">
<field name="name">Yönetici / Admin (MyMach)</field>
<field name="implied_ids" eval="[(4, ref('group_mymach_fleet_user'))]"/>
<field name="user_ids" eval="[(4, ref('base.user_admin')), (4, ref('base.user_root'))]"/>
</record>
<!-- RECORD RULES (Sözleşme) -->
<record id="rule_mymach_fleet_contract_user" model="ir.rule">
<field name="name">Sürücüler kendi sözleşmesini görür</field>
<field name="model_id" ref="model_mymach_fleet_contract"/>
<field name="domain_force">[('driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_contract_manager" model="ir.rule">
<field name="name">Yöneticiler tüm sözleşmeleri görür</field>
<field name="model_id" ref="model_mymach_fleet_contract"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Rezervasyon) -->
<record id="rule_mymach_fleet_reservation_user" model="ir.rule">
<field name="name">Sürücüler kendi taleplerini görür</field>
<field name="model_id" ref="model_mymach_fleet_reservation"/>
<field name="domain_force">[('create_uid', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_reservation_manager" model="ir.rule">
<field name="name">Yöneticiler tüm talepleri görür</field>
<field name="model_id" ref="model_mymach_fleet_reservation"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Teslimat / Handover) -->
<record id="rule_mymach_fleet_handover_user" model="ir.rule">
<field name="name">Sürücüler kendi teslimatlarını görür</field>
<field name="model_id" ref="model_mymach_fleet_handover"/>
<field name="domain_force">[('driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_handover_manager" model="ir.rule">
<field name="name">Yöneticiler tüm teslimatları görür</field>
<field name="model_id" ref="model_mymach_fleet_handover"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Güvenlik / Rota Sapma Logları) -->
<record id="rule_mymach_fleet_security_log_user" model="ir.rule">
<field name="name">Sürücüler kendi rota sapma uyarılarını görür</field>
<field name="model_id" ref="model_mymach_fleet_security_log"/>
<field name="domain_force">[('vehicle_id.driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_security_log_manager" model="ir.rule">
<field name="name">Yöneticiler tüm güvenlik ihlallerini görür</field>
<field name="model_id" ref="model_mymach_fleet_security_log"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Cezalar ve Giderler) -->
<record id="rule_mymach_fleet_fine_user" model="ir.rule">
<field name="name">Sürücüler kendi cezalarını görür</field>
<field name="model_id" ref="model_mymach_fleet_fine"/>
<field name="domain_force">[('driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_fine_manager" model="ir.rule">
<field name="name">Yöneticiler tüm cezaları görür</field>
<field name="model_id" ref="model_mymach_fleet_fine"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
</record>
<!-- RECORD RULES (Periyodik Bakım) -->
<record id="rule_mymach_fleet_periodic_maintenance_user" model="ir.rule">
<field name="name">Sürücüler kendi araçlarının bakımlarını görür</field>
<field name="model_id" ref="model_mymach_fleet_periodic_maintenance"/>
<field name="domain_force">[('vehicle_id.driver_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_user'))]"/>
</record>
<record id="rule_mymach_fleet_periodic_maintenance_manager" model="ir.rule">
<field name="name">Yöneticiler tüm bakımları görür</field>
<field name="model_id" ref="model_mymach_fleet_periodic_maintenance"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_mymach_fleet_manager'))]"/>
<field name="implied_ids" eval="[(4, ref('mymach_fleet_intelligence.group_mymach_fleet_user'))]"/>
</record>
</data>
</odoo>

View File

@@ -82,7 +82,6 @@
<!-- Ana Menü ve Alt Menüler -->
<menuitem id="menu_mymach_fleet_intelligence_root"
name="MyMach Filo Zekası"
action="action_mymach_fleet_home_server"
web_icon="fleet,static/description/icon.png"
sequence="10"/>

View File

@@ -0,0 +1,282 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="driver_tracker_template" name="MyMach Sürücü Mobil Takip">
<t t-call="web.html_container">
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
<title>Şoför Konum Takibi</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"/>
<style>
body {
background-color: #0f172a;
color: #f8fafc;
font-family: 'Inter', -apple-system, sans-serif;
height: 100vh;
display: flex;
flex-direction: column;
margin: 0;
overflow: hidden;
}
.header {
background-color: #1e293b;
padding: 20px;
text-align: center;
border-bottom: 1px solid #334155;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.header h2 {
margin: 0;
font-size: 20px;
font-weight: 600;
color: #e2e8f0;
}
.main-container {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
}
.vehicle-selector {
width: 100%;
max-width: 350px;
margin-bottom: 30px;
}
.form-select {
background-color: #1e293b;
color: #f8fafc;
border: 1px solid #475569;
border-radius: 12px;
padding: 15px;
font-size: 16px;
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
}
.form-select:focus {
background-color: #1e293b;
color: white;
border-color: #3b82f6;
box-shadow: 0 0 0 0.25rem rgba(59, 130, 246, 0.25);
}
.btn-track {
width: 220px;
height: 220px;
border-radius: 50%;
font-size: 24px;
font-weight: 700;
border: none;
color: white;
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
box-shadow: 0 10px 25px -5px rgba(59, 130, 246, 0.5), inset 0 4px 6px -1px rgba(255, 255, 255, 0.2);
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 15px;
}
.btn-track:active {
transform: scale(0.95);
}
.btn-track.active {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
box-shadow: 0 10px 25px -5px rgba(239, 68, 68, 0.5), inset 0 4px 6px -1px rgba(255, 255, 255, 0.2);
animation: pulse-red 2s infinite;
}
@keyframes pulse-red {
0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); }
70% { box-shadow: 0 0 0 20px rgba(239, 68, 68, 0); }
100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
}
.btn-track i {
font-size: 50px;
}
.status-panel {
margin-top: 40px;
text-align: center;
width: 100%;
max-width: 350px;
background-color: #1e293b;
padding: 15px;
border-radius: 12px;
border: 1px solid #334155;
}
.status-text {
font-size: 16px;
font-weight: 500;
color: #94a3b8;
margin-bottom: 5px;
}
.coords-text {
font-size: 14px;
color: #cbd5e1;
font-family: monospace;
}
.last-ping {
font-size: 12px;
color: #10b981;
margin-top: 10px;
display: none;
}
</style>
</head>
<body>
<div class="header">
<h2><i class="fa-solid fa-truck-fast"></i> Sürücü Konum Paneli</h2>
</div>
<div class="main-container">
<div class="vehicle-selector">
<label class="form-label" style="color: #94a3b8; font-size: 14px; font-weight: 500; margin-bottom: 8px;">Kullanılan Araç:</label>
<select id="vehicle_select" class="form-select">
<t t-foreach="vehicles" t-as="v">
<option t-att-value="v.id"><t t-esc="v.license_plate"/> <t t-if="v.name">- <t t-esc="v.name"/></t></option>
</t>
</select>
</div>
<button id="toggleTrackBtn" class="btn-track">
<i class="fa-solid fa-location-crosshairs" id="trackIcon"></i>
<span id="trackLabel">SEFERE BAŞLA</span>
</button>
<div class="status-panel">
<div class="status-text" id="statusText">Konum takibi kapalı.</div>
<div class="coords-text" id="coordsText">-- / --</div>
<div class="last-ping" id="lastPingText"><i class="fa-solid fa-check-circle"></i> Son Gönderim: <span></span></div>
</div>
</div>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
const btn = document.getElementById('toggleTrackBtn');
const trackLabel = document.getElementById('trackLabel');
const trackIcon = document.getElementById('trackIcon');
const statusText = document.getElementById('statusText');
const coordsText = document.getElementById('coordsText');
const lastPingText = document.getElementById('lastPingText');
const lastPingSpan = lastPingText.querySelector('span');
const vehicleSelect = document.getElementById('vehicle_select');
let isTracking = false;
let watchId = null;
const API_TOKEN = 'MYMACH_SECURE_TOKEN_2026';
function formatTime(date) {
return date.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
}
function sendPing(lat, lng) {
const vehicleId = parseInt(vehicleSelect.value);
if (!vehicleId) return;
const payload = {
jsonrpc: "2.0",
method: "call",
params: {
vehicle_id: vehicleId,
latitude: lat,
longitude: lng,
api_token: API_TOKEN
}
};
fetch('/mymach/fleet/ping', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
}).then(res => res.json())
.then(data => {
console.log('Ping Success:', data);
lastPingText.style.display = 'block';
lastPingSpan.textContent = formatTime(new Date());
}).catch(err => {
console.error('Ping Error:', err);
});
}
function successPosition(position) {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
coordsText.textContent = lat.toFixed(6) + " / " + lng.toFixed(6);
sendPing(lat, lng);
}
function errorPosition(err) {
console.warn(`ERROR(${err.code}): ${err.message}`);
statusText.textContent = "Hata: Konum alınamadı!";
statusText.style.color = "#ef4444";
stopTracking();
}
function startTracking() {
if (!navigator.geolocation) {
alert("Tarayıcınız konum servisini desteklemiyor.");
return;
}
isTracking = true;
btn.classList.add('active');
trackLabel.textContent = "SEFERİ BİTİR";
trackIcon.className = "fa-solid fa-stop";
statusText.textContent = "Konum canlı takip ediliyor...";
statusText.style.color = "#10b981";
vehicleSelect.disabled = true;
// watchPosition keeps getting location updates
watchId = navigator.geolocation.watchPosition(successPosition, errorPosition, {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
});
}
function stopTracking() {
isTracking = false;
btn.classList.remove('active');
trackLabel.textContent = "SEFERE BAŞLA";
trackIcon.className = "fa-solid fa-location-crosshairs";
statusText.textContent = "Konum takibi durduruldu.";
statusText.style.color = "#94a3b8";
vehicleSelect.disabled = false;
lastPingText.style.display = 'none';
if (watchId !== null) {
navigator.geolocation.clearWatch(watchId);
watchId = null;
}
}
btn.addEventListener('click', function() {
if (isTracking) {
stopTracking();
} else {
startTracking();
}
});
});
</script>
</body>
</html>
</t>
</template>
<!-- Odoo Menüsü İçin URL Aksiyonu ve Menü Öğesi -->
<record id="action_mymach_driver_tracker_url" model="ir.actions.act_url">
<field name="name">Sürücü Mobil Takip Ekranı</field>
<field name="url">/mymach/driver/tracker</field>
<field name="target">new</field>
</record>
<menuitem id="menu_mymach_driver_tracker"
name="Sürücü Mobil Takip"
parent="hr.menu_hr_root"
action="action_mymach_driver_tracker_url"
sequence="15"/>
</odoo>

View File

@@ -25,6 +25,12 @@
<field name="is_maintenance_critical" invisible="1"/>
<widget name="web_ribbon" title="SATMAK DAHA KARLI!" bg_color="bg-danger" invisible="not is_maintenance_critical"/>
</xpath>
<!-- Faz 2.3 ve 2.6: Araç Türü ve Şirket Lokasyonu (Standart Text Location Yerine) -->
<xpath expr="//field[@name='location']" position="replace">
<field name="mymach_vehicle_type" />
<field name="mymach_location_id" />
</xpath>
<xpath expr="//notebook" position="inside">
<page string="Finans &amp; Gider Analizi (KM / Aylık / Yıllık)">
<group string="Para Birimi &amp; Listbox Gösterim Ayarları">

View File

@@ -60,12 +60,7 @@ action = {
<!-- Floating Content Wrapper -->
<div class="mymach_zara_content" style="position: relative;">
<!-- Dil Seçici (Top Right) -->
<div style="position: absolute; top: 10px; right: 15px; width: 140px; z-index: 100;">
<field name="dashboard_lang_id" widget="selection" style="background: rgba(15, 23, 42, 0.6) !important; color: white !important; border: 1px solid rgba(255, 255, 255, 0.15) !important; border-radius: 6px; padding: 4px; width: 100%; display: block; margin-bottom: 5px;"/>
<button name="action_apply_language" type="object" string="🌐 Dili Değiştir" class="btn btn-sm btn-outline-light" style="width: 100%; font-size: 11px; padding: 3px;"/>
</div>
<!-- Dil Seçici Kaldırıldı (Odoo Standardı Kullanılacak) -->
<!-- Zara-Style Minimalist Editorial Header -->
<div style="text-align: center; margin-bottom: 10px; border-bottom: 1px solid rgba(255,255,255,0.08); padding-bottom: 25px;">
<h1 class="mymach_zara_title" style="color: #ffffff !important; text-shadow: 0 0 15px rgba(255, 255, 255, 0.5); font-weight: 800;">MYMACH FLEET INTELLIGENCE</h1>
@@ -216,7 +211,14 @@ action = {
</field>
</record>
<!-- Odoo'nun Cache sorunlarını aşmak için Root Menu yerine Alt Menü (Sequence 0) ekliyoruz -->
<!-- Root Menüye Ana Pano Aksiyonunu Bağlıyoruz -->
<menuitem id="menu_mymach_fleet_intelligence_root"
name="MyMach Filo Zekası"
action="action_mymach_fleet_home_server"
web_icon="fleet,static/description/icon.png"
sequence="10"/>
<!-- Alt Menü: Ana Pano -->
<menuitem id="menu_mymach_fleet_home"
name="Ana Pano"
parent="menu_mymach_fleet_intelligence_root"

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_users_form_mymach_inherit" model="ir.ui.view">
<field name="name">res.users.form.mymach.inherit</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook/page[@name='access_rights']//group[1]" position="inside">
<group string="MYMACH FİLO">
<field name="mymach_role"/>
</group>
</xpath>
</field>
</record>
</odoo>