Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f4216d790c | |||
| eb17894a13 |
|
|
@ -1,5 +1 @@
|
|||
from . import models
|
||||
from . import controllers
|
||||
from . import post_init_hook
|
||||
# damit run_migration sichtbar ist:
|
||||
run_migration = post_init_hook.run_migration
|
||||
|
|
@ -1,38 +1,19 @@
|
|||
{
|
||||
'name': 'POS Open Workshop',
|
||||
'license': 'AGPL-3',
|
||||
'version': '13.0.1.0.0',
|
||||
'version': '16.0.1.0.0',
|
||||
'summary': 'Erstellt Maschinenfreigaben basierend auf POS-Einweisungsprodukten',
|
||||
'depends': ['base','product','sale','contacts','point_of_sale'],
|
||||
'author': 'matthias.lotz',
|
||||
'category': 'Point of Sale',
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/machine_product_training_views.xml',
|
||||
'views/menu_views.xml',
|
||||
'views/machine_area_views.xml',
|
||||
'views/machine_views.xml',
|
||||
'views/res_partner_view.xml',
|
||||
'views/assets.xml',
|
||||
'data/data.xml',
|
||||
],
|
||||
'qweb': [
|
||||
'static/src/xml/ows_briefing_details.xml',
|
||||
'static/src/xml/ows_briefing_details_edit.xml',
|
||||
'static/src/xml/ows_pos_order_selector.xml',
|
||||
'static/src/xml/ows_machine_sidebar.xml',
|
||||
'static/src/xml/ows_pos_machine_access_view.xml',
|
||||
],
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
|
||||
'installable': True,
|
||||
'assets': {
|
||||
'point_of_sale.assets': [
|
||||
'static/src/js/machine_access_sidebar.js',
|
||||
'static/src/css/pos.css',
|
||||
],
|
||||
},
|
||||
'post_init_hook': 'run_migration',
|
||||
'description': """
|
||||
Diese App erstellt Maschinenfreigaben basierend auf POS-Einweisungsprodukten.
|
||||
Die App ist für den Einsatz in der Odoo-Version 13.0 konzipiert.
|
||||
Die App ist für den Einsatz in der Odoo-Version 16.0 konzipiert.
|
||||
""",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
# Datei: open_workshop/controllers/__init__.py
|
||||
|
||||
from . import pos_access
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
# Datei: controllers/pos_access.py
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
class OpenWorkshopPOSController(http.Controller):
|
||||
|
||||
@http.route('/open_workshop/partner_access', type='json', auth='user')
|
||||
def get_partner_machine_access(self, partner_id):
|
||||
Machine = request.env['ows.machine'].sudo()
|
||||
return Machine.get_access_list_grouped(partner_id)
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
id,name,street,zip,city,phone,email,company_type,customer_rank,supplier_rank
|
||||
res_partner_demo_1, AAAA Max Mustermann,Musterstraße 1,,,,,person,15,0
|
||||
res_partner_demo_2, Benjamin Winter,,,,,,person,1,0
|
||||
res_partner_demo_3, Martin Berthelon,Fabrikstr. 3,73728,Esslingen,,martin.berthelon@hotmail.fr,person,15,0
|
||||
res_partner_demo_4,Aaron Christ,Hohewartstraße 46,70469,Stuttgart,,christ.aaron@web.de,person,14,0
|
||||
res_partner_demo_5,Aaron Dörr,Riegeläckerstr. 60,71229,Leonberg,,aaron_doerr@web.de,person,33,0
|
||||
res_partner_demo_6,Aaron Gale,Chopinstr. 20,70195,Stuttgart,015172165290,aarongale1@live.com,person,4,0
|
||||
res_partner_demo_7,Aaron Zimmermann,Heinrichstr. 15,38106 ,Braunschweig,016091647469,,person,1,0
|
||||
res_partner_demo_8,Abalrahman Alsadi,Bachstr. 29,70563,Stuttgart,,abdulrahman.m.saadi@gmail.com,person,1,0
|
||||
res_partner_demo_9,Abdullah Zengin,Engelbertstr. 124,70499,Stuttgart,,,person,3,0
|
||||
res_partner_demo_10,Abdussamed Korkmaz,Bertha-von-Suttner-Straße 1,74366,Kirchheim Am Neckar,,korkmaz.abdussamed@gmail.com,person,1,0
|
||||
res_partner_demo_11,Achim Brendle,Oberwiesenstraße 45,70619,Stuttgart,7114797505,achim.brendle@web.de,person,2,0
|
||||
res_partner_demo_12,Achim Jatkowski,Hummelstr. 38,70569,Stuttgart,017621512316,achim.jatkowski@gmail.com,person,1,0
|
||||
res_partner_demo_13,Achim Jung,Kurt Tucholsky Str. 6,71254,Ditzingen,07156174013,acjung@web.de,person,1,0
|
||||
res_partner_demo_14,Achim Kelbel,Vivaldiweg 6,70195,Stuttgart,,a.kelbel@t-online.de,person,2,0
|
||||
res_partner_demo_15,Achim Kramer,Reinsburger 172,70197,Stuttgart,,achim@zibra.de,person,1,0
|
||||
res_partner_demo_16,Adalbert Zeisl,Bachstr. 20,71364,Winnenden,07195-2092884,betz1000@gmx.de,person,2,0
|
||||
res_partner_demo_17,Adalina Schäfer,Sancenbacherstr. 26,74538,Rosengarten,015778855550,lina_max_schaefer@gmx.de,person,1,0
|
||||
res_partner_demo_18,Adam Riegel,Marabustr. 35 / 84,70378,Stuttgart,0711 532082,,person,1,0
|
||||
res_partner_demo_19,Adam Swais,Obertürkheimerstr. 54,73733,Esslingen,,adamswais@web.de,person,1,0
|
||||
res_partner_demo_20,Adela Spulber,Obere Bismarck Str. 97,70197,Stuttgart,,,person,1,0
|
||||
res_partner_demo_21,Adem Uzun,Liesel-Bach-Str. 54,71034,Böblingen,015251690873,adem.uzun2@gmail.com,person,1,0
|
||||
res_partner_demo_22,Adnan Djekic,Vesoulerstr. 33,70839,Gerlingen,01724227468,adnandjekic@alice-dsl.net,person,1,0
|
||||
res_partner_demo_23,Adrian Berres,Bärgstadter Str. 90,63928,Gehenbühl,,a.berres@gmx.de,person,1,0
|
||||
res_partner_demo_24,Adrian Lanksweirt,Heidestraße 6,70469,Stuttgart,,adrian.lanksweirt@gmail.com,person,1,0
|
||||
res_partner_demo_25,Adrian Popov,Hallerstr. 42,90419,Nürnberg,+4915114305751,adrinuernberg@gmail.com,person,2,0
|
||||
res_partner_demo_26,Agnes Krettek,Seyfferstr. 62,70187,Stuttgart,,agneskrettek@gmail.com,person,1,0
|
||||
res_partner_demo_27,Ahmad Taijan,Rümelinstr 69,70191,Stuttgart,,,person,2,0
|
||||
res_partner_demo_28,Aileen Becker,Eichendorffstr. 4,73630,Remshalden,015780645637,aileen.becker@gmx.de,person,87,0
|
||||
res_partner_demo_29,Ailey Simpson,Eierstraße 44 A,70199,Stuttgart,,aileywsimpson@gmail.com,person,1,0
|
||||
res_partner_demo_30,Akira Mitsu,Fritz-Ulrich-Weg 5,70567,Stuttgart,,mitsuakira0914@gmail.com,person,5,0
|
||||
res_partner_demo_31,Aksel Özdemir,Rotebühlstraße 53,70178,Stuttgart,,aksel.oezdemir@gmx.de,person,2,0
|
||||
res_partner_demo_32,Albert Ebenbichler,Am Backhaus 9,73666,Boltmannsweiler,01726101655,info@albert-ebenbichler.com,person,1,0
|
||||
res_partner_demo_33,Albert Kaupp,Waldäckerstr. 10,70435,Stuttgart,0711 8263232,albert.kaupp@online.de,person,2,0
|
||||
res_partner_demo_34,Albrecht Barth,Klopstockstr. 39,70193,Stuttgart,,albrecht.barth@web.de,person,3,0
|
||||
res_partner_demo_35,Albrecht Schlayer,Im Netzbrunnen 17,70825,K-Münchingen,,aws1308@gmail.com,person,1,0
|
||||
res_partner_demo_36,Alec Dobler,Kräherwald 251,70193,Stuttgart,,,person,1,0
|
||||
res_partner_demo_37,Alejandro Cano Perez,Burgstallstraße 66,70199,Stuttgart,,cano.perez@gmx.de,person,2,0
|
||||
res_partner_demo_38,Alejandro Rodriguez,Im Hirschwinkel 1,76297,Stutensee,015771409317,ralexei95@yahoo.de,person,1,0
|
||||
res_partner_demo_39,Alejandro Zarza Aguado,Reinsburgstr. 152,70197,Stuttgart,017628401435,11alex96@gmail.com,person,1,0
|
||||
res_partner_demo_40,Aleksandar Vasić,Lothringer Str. 5,70435,Stuttgart,,aleksvasic@web.de,person,3,0
|
||||
res_partner_demo_41,Alen Minasyan,Kastanienallee 41/1,71638,Ludwigsburg,,bidilik@gmx.de,person,1,0
|
||||
res_partner_demo_42,Alex Olenberg,Theodor-Rottschildstr. 25,73760,Stuttgart,,,person,26,0
|
||||
res_partner_demo_43,Alex Schaut,Braunenbergweg 9,70806,Kornwestheim,07154 16530,aschaut@gmx.de,person,3,0
|
||||
res_partner_demo_44,Alexander Adloff,Charlottenstraße 2,74074,Heilbronn,,alexadloff@gmx.de,person,3,0
|
||||
res_partner_demo_45,Alexander Bauer,Im Himmel 20,70569,Stuttgart,071172237601,ab.312@icloud.com,person,1,0
|
||||
res_partner_demo_46,Alexander Blendl,Neckarstr. 8,70736,Fellbach,,blendl.alex@gmail.com,person,4,0
|
||||
res_partner_demo_47,Alexander Borshov,Schellingstraße 24,71277,Rutesheim,,aborshov@gmail.com,person,1,0
|
||||
res_partner_demo_48,Alexander Bosch,Osterwiesenstr. 37,70794,Filderstadt,,bosch-alexander@web.de,person,1,0
|
||||
res_partner_demo_49,Alexander Braig,Holzgrund Str. 25,70806,Kornwestheim,,a.braig84@gmx.de,person,17,0
|
||||
res_partner_demo_50,Alexander Carolus,Kornbergstr. 23,70176,Stuttgart,,alexander.carolus,person,1,0
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
/opt/odoo/odoo/odoo-bin shell -d hobbyhimmel < /home/odoo/custom_addons/open_workshop/demo/export_partner.py
|
||||
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
import csv
|
||||
import random
|
||||
|
||||
# Beispielsweise 50 Kunden mit Namen und E-Mail
|
||||
partners = env['res.partner'].search(
|
||||
[('customer_rank', '>', 0), ('is_company', '=', False)],
|
||||
limit=50
|
||||
)
|
||||
|
||||
with open('/home/odoo/custom_addons/open_workshop/demo/demo_partners.csv', 'w', newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow([
|
||||
'id',
|
||||
'name',
|
||||
'street',
|
||||
'zip',
|
||||
'city',
|
||||
'phone',
|
||||
'email',
|
||||
'company_type',
|
||||
'customer_rank',
|
||||
'supplier_rank'
|
||||
])
|
||||
|
||||
for idx, partner in enumerate(partners, start=1):
|
||||
partner_id = f'res_partner_demo_{idx}'
|
||||
writer.writerow([
|
||||
partner_id,
|
||||
partner.name or '',
|
||||
partner.street or '',
|
||||
partner.zip or '',
|
||||
partner.city or '',
|
||||
partner.phone or '',
|
||||
partner.email or '',
|
||||
partner.company_type or 'person',
|
||||
partner.customer_rank,
|
||||
partner.supplier_rank,
|
||||
])
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from odoo import SUPERUSER_ID
|
||||
from odoo.api import Environment
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
MISSING_PARTNERS = [
|
||||
6534, 1594, 4700, 6557, 5392, 4960, 5226, 6535, 4666
|
||||
]
|
||||
|
||||
def insert_missing_partners(cr, registry):
|
||||
env = Environment(cr, SUPERUSER_ID, {})
|
||||
|
||||
for partner_id in MISSING_PARTNERS:
|
||||
cr.execute("""
|
||||
INSERT INTO res_partner (
|
||||
id, name, customer_rank, create_uid, create_date, write_uid, write_date
|
||||
)
|
||||
VALUES (%s, %s, 1, %s, now(), %s, now())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
""", (partner_id, f"Fehlender Partner {partner_id}", SUPERUSER_ID, SUPERUSER_ID))
|
||||
|
||||
cr.execute("SELECT setval('res_partner_id_seq', (SELECT MAX(id) FROM res_partner));")
|
||||
|
||||
_logger.info(f"[OWS Repair] {len(MISSING_PARTNERS)} fehlende Partner hinzugefügt.")
|
||||
cr.commit()
|
||||
|
||||
# Automatischer Start in odoo-bin shell
|
||||
if 'env' in globals():
|
||||
insert_missing_partners(env.cr, env.registry)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from . import ows_models
|
||||
from . import pos_order
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
from odoo import models, fields, api
|
||||
|
||||
#import debugpy
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
_logger.info("✅ pos_order.py geladen")
|
||||
|
||||
# debugpy.listen(("0.0.0.0", 5678))
|
||||
print("✅ debugpy wartet auf Verbindung (Port 5678) ...")
|
||||
# Optional: Starte erst, wenn VS Code verbunden ist
|
||||
#debugpy.wait_for_client()
|
||||
|
||||
class PosOrder(models.Model):
|
||||
_inherit = 'pos.order'
|
||||
|
||||
def _process_order(self, order, draft, existing_order):
|
||||
pos_order_id = super(PosOrder, self)._process_order(order, draft, existing_order)
|
||||
pos_order = self.browse(pos_order_id)
|
||||
|
||||
training_products = self.env['ows.machine.training'].search([])
|
||||
product_map = {
|
||||
tp.training_id.product_tmpl_id.id: tp.machine_id.id
|
||||
for tp in training_products
|
||||
}
|
||||
|
||||
partner = pos_order.partner_id
|
||||
if not partner:
|
||||
_logger.info("🟡 POS-Bestellung ohne Partner – keine Freigabe möglich")
|
||||
return pos_order_id
|
||||
|
||||
for line in pos_order.lines:
|
||||
product_tmpl_id = line.product_id.product_tmpl_id.id
|
||||
machine_id = product_map.get(product_tmpl_id)
|
||||
|
||||
_logger.info("🔍 Prüfe Produkt %s → Maschine ID: %s", line.product_id.display_name, machine_id)
|
||||
|
||||
if machine_id:
|
||||
already_exists = self.env['ows.machine.access'].search([
|
||||
('partner_id', '=', partner.id),
|
||||
('machine_id', '=', machine_id)
|
||||
])
|
||||
if not already_exists:
|
||||
self.env['ows.machine.access'].create({
|
||||
'partner_id': partner.id,
|
||||
'machine_id': machine_id,
|
||||
'granted_by_pos': True
|
||||
})
|
||||
_logger.info("✅ Maschinenfreigabe erstellt: %s für %s", machine_id, partner.name)
|
||||
|
||||
return pos_order_id
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
from odoo import SUPERUSER_ID
|
||||
|
||||
def fix_missing_pos_order_partners(cr, registry):
|
||||
"""
|
||||
Findet POS-Bestellungen mit fehlendem Partner und fügt Dummy-Partner
|
||||
direkt per SQL mit der passenden ID ein.
|
||||
"""
|
||||
cr.execute("""
|
||||
SELECT DISTINCT partner_id FROM pos_order
|
||||
WHERE partner_id IS NOT NULL
|
||||
AND partner_id NOT IN (SELECT id FROM res_partner)
|
||||
""")
|
||||
missing_ids = [row[0] for row in cr.fetchall()]
|
||||
print(f"Superuser: {SUPERUSER_ID}")
|
||||
if not missing_ids:
|
||||
print("✅ Keine fehlenden Partner gefunden.")
|
||||
return
|
||||
|
||||
print(f"🚧 Fehlende Partner-IDs: {missing_ids}")
|
||||
|
||||
# Direkter SQL-Insert für res_partner
|
||||
for pid in missing_ids:
|
||||
print(f"➕ Erzeuge Dummy-Partner mit ID {pid}")
|
||||
cr.execute("""
|
||||
INSERT INTO res_partner (id, name, customer_rank, create_uid, create_date, write_uid, write_date)
|
||||
VALUES (%s, %s, %s, %s, now(), %s, now())
|
||||
ON CONFLICT (id) DO NOTHING
|
||||
""", (pid, f"Fehlender Partner {pid}", 1, SUPERUSER_ID, SUPERUSER_ID))
|
||||
|
||||
# Sequenz zurücksetzen, um ID-Kollision zu verhindern
|
||||
cr.execute("""
|
||||
SELECT setval('res_partner_id_seq', (SELECT MAX(id) FROM res_partner))
|
||||
""")
|
||||
print("✅ Alle fehlenden Partner ergänzt.")
|
||||
|
||||
|
||||
|
||||
# Automatischer Start in odoo-bin shell
|
||||
if 'env' in globals():
|
||||
fix_missing_pos_order_partners(env.cr, env.registry)
|
||||
env.cr.commit()
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
# import_machine_products.py
|
||||
# odoo-bin shell -d deine_datenbank < import_machine_products.py
|
||||
import logging
|
||||
from odoo import api, SUPERUSER_ID
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# Mapping von product_id > machine_id
|
||||
TRAINING_MAPPING = {
|
||||
'16':'1',
|
||||
'11':'2',
|
||||
'10':'4',
|
||||
'117':'5',
|
||||
'14':'6',
|
||||
'12':'7',
|
||||
'15':'8',
|
||||
'118':'10',
|
||||
'118':'11',
|
||||
'118':'12',
|
||||
'18':'15',
|
||||
'18':'16',
|
||||
'13':'18',
|
||||
'17':'19',
|
||||
}
|
||||
|
||||
# Mapping von product_id > machine_id
|
||||
USAGE_MAPPING = {
|
||||
'50':'1',
|
||||
'49':'2',
|
||||
'49':'3',
|
||||
'49':'4',
|
||||
'53':'5',
|
||||
'52':'7',
|
||||
'55':'8',
|
||||
'59':'15',
|
||||
'60':'15',
|
||||
'59':'16',
|
||||
'60':'16',
|
||||
'57':'18',
|
||||
'58':'19',
|
||||
}
|
||||
|
||||
def run_import(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
|
||||
machine_model = env['ows.machine']
|
||||
product_model = env['product.product']
|
||||
training_model = env['ows.machine.training']
|
||||
usage_model = env['ows.machine.product']
|
||||
|
||||
created_training = 0
|
||||
created_usage = 0
|
||||
|
||||
for product_id, machine_id in TRAINING_MAPPING.items():
|
||||
machine = machine_model.search([('id', '=', machine_id)], limit=1)
|
||||
product = product_model.search([('id', '=', product_id)], limit=1)
|
||||
if machine and product:
|
||||
training_model.create({
|
||||
'machine_id': machine.id,
|
||||
'training_id': product.id,
|
||||
})
|
||||
created_training += 1
|
||||
|
||||
for product_id, machine_id in USAGE_MAPPING.items():
|
||||
machine = machine_model.search([('id', '=', machine_id)], limit=1)
|
||||
product = product_model.search([('id', '=', product_id)], limit=1)
|
||||
if machine and product:
|
||||
usage_model.create({
|
||||
'machine_id': machine.id,
|
||||
'product_id': product.id,
|
||||
})
|
||||
created_usage += 1
|
||||
|
||||
_logger.info(f"[OWS Import] ✅ Trainings-Zuordnungen erstellt: {created_training}, Nutzungs-Zuordnungen erstellt: {created_usage}")
|
||||
env.cr.commit()
|
||||
|
||||
# Automatischer Start in odoo-bin shell
|
||||
if 'env' in globals():
|
||||
run_import(env.cr, env.registry)
|
||||
|
|
@ -1 +0,0 @@
|
|||
/opt/odoo/odoo/odoo-bin -d hobbyhimmel13_dev -i base,sale,pos_time_based_products,wk_coupons,pos_coupons,open_workshop --stop-after-init --load-language=de_DE
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
#!/bin/bash
|
||||
BACKUP_DIR=/root # Verzeichnis, in dem das Backup temporär gespeichert wird.
|
||||
#MONITOR_URL='https://cronitor.link/p/c6debfe4e00f46fe9eb430ab9d2b2588/7PPIpd' # Cronitor-URL zur Überwachung des Skriptstatus.
|
||||
ODOO_DATABASE=hobbyhimmel # Der Name der Odoo-Datenbank, die wiederhergestellt werden soll.
|
||||
DEFAULT_URL="http://hobbybackend2.fritz.box:9013" # Standard-URL für die Odoo-Datenbank-Backup-API.
|
||||
URL="${1:-$DEFAULT_URL}" # Falls ein Argument übergeben wird, wird es als URL genutzt, sonst bleibt die Standard-URL.
|
||||
BACKUP_NAME=${ODOO_DATABASE}.latest.zip # Der Name der Backup-Datei, wobei das aktuelle Datum an den Namen angehängt wird.
|
||||
remote_directory=/www/htdocs/${sftp_user}/odoo # Das Verzeichnis auf dem Remote-SFTP-Server, in dem die Backups gespeichert werden.
|
||||
|
||||
# Function to report failure to Cronitor with step
|
||||
# Diese Funktion meldet einen Fehler an Cronitor und beendet das Skript.
|
||||
# Parameter: $1 - Der Schrittname, der beim Fehlschlag gemeldet wird.
|
||||
report_failure() {
|
||||
local step=$1 # Schrittname wird als erster Parameter übergeben.
|
||||
echo "Error during $step" # Meldet den Fehler.
|
||||
#curl "$MONITOR_URL?state=fail&message=$step" # Meldet den Fehlerstatus an Cronitor.
|
||||
exit 1 # Beendet das Skript mit Exit-Code 1 (Fehler).
|
||||
}
|
||||
|
||||
echo "Restoring Odoo database from backup..." # Meldet den Beginn der Wiederherstellung der Odoo-Datenbank.
|
||||
echo "Verbindungsdaten: ${sftp_password}, ssh-${sftp_user}, ${sftp_host}"
|
||||
|
||||
cd /root # Wechselt in das Home-Verzeichnis des Benutzers.
|
||||
# Holt die verschlüsselte Backup-Datei vom Remote-SFTP-Server in das lokale Verzeichnis.
|
||||
sshpass -p "${sftp_password}" sftp -i ~/.ssh/id_rsa ssh-${sftp_user}@${sftp_host}:${remote_directory} <<EOF
|
||||
get ${BACKUP_NAME}.gpg
|
||||
get ${BACKUP_NAME}
|
||||
EOF
|
||||
# Prüft, ob der vorherige Befehl erfolgreich war, sonst bricht das Skript ab.
|
||||
if [ $? -ne 0 ]; then
|
||||
report_failure "SFTP transfer"
|
||||
fi
|
||||
cd /root
|
||||
|
||||
# Backup entschlüsseln -> Funktioniert nicht, weil im Passwort ein ´ enthalten ist
|
||||
#rm ${BACKUP_NAME}
|
||||
#gpg --batch --yes --passphrase "{$gpg_password}" --output "${BACKUP_DIR}/${BACKUP_NAME}" --decrypt "${BACKUP_DIR}/${BACKUP_NAME}.gpg"
|
||||
|
||||
echo "Admin password: ${ADMIN_PASSWORD}" # Meldet das Admin-Passwort.
|
||||
echo "Database name: ${ODOO_DATABASE}" # Meldet den Namen der Odoo-Datenbank.
|
||||
echo "Backup name: ${BACKUP_NAME}" # Meldet den Namen der Backup-Datei.
|
||||
echo "URL: ${URL}" # Meldet die URL der Odoo-Datenbank-Backup-API.
|
||||
echo "Backup directory: ${BACKUP_DIR}" # Meldet das Verzeichnis, in dem das Backup gespeichert wird.
|
||||
echo "Delete database: ${ODOO_DATABASE}" # Meldet, dass die Datenbank gelöscht wird.
|
||||
curl -X POST -s \
|
||||
-F "master_pwd=${ADMIN_PASSWORD}" \
|
||||
-F "name=${ODOO_DATABASE}" \
|
||||
${URL}/web/database/drop || report_failure "Database deletion"
|
||||
|
||||
echo "Restoring database..." # Meldet den Beginn der Wiederherstellung der Datenbank.
|
||||
curl \
|
||||
-F "master_pwd=${ADMIN_PASSWORD}" \
|
||||
-F "name=${ODOO_DATABASE}" \
|
||||
-F "backup_file=@${BACKUP_DIR}/${BACKUP_NAME}" \
|
||||
-F "backup_format=zip" \
|
||||
-F "copy=true" \
|
||||
${URL}/web/database/restore || report_failure "Database restoration"
|
||||
|
||||
# If everything was successful, report completion
|
||||
# Meldet den erfolgreichen Abschluss des Skripts an Cronitor.
|
||||
#curl "$MONITOR_URL?state=complete"
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
import sys
|
||||
import os
|
||||
import xmlrpc.client
|
||||
|
||||
# ----------------------
|
||||
# KONFIGURATION AUS UMGEBUNG
|
||||
# ----------------------
|
||||
url = os.getenv("ODOO_URL", "http://localhost:8069")
|
||||
db = os.getenv("ODOO_DB", "hobbyhimmel")
|
||||
username = os.getenv("ODOO_USERNAME")
|
||||
password = os.getenv("ODOO_PASSWORD")
|
||||
|
||||
# ----------------------
|
||||
# PRÜFUNG DER KONFIGURATION
|
||||
# ----------------------
|
||||
if not all([url, db, username, password]):
|
||||
print("❌ Fehler: ODOO_URL, ODOO_DB, ODOO_USERNAME und ODOO_PASSWORD müssen als Umgebungsvariablen gesetzt sein.")
|
||||
sys.exit(1)
|
||||
|
||||
# ----------------------
|
||||
# PARAMETER PRÜFEN
|
||||
# ----------------------
|
||||
if len(sys.argv) != 2:
|
||||
print("❌ Fehler: Modulname muss als Parameter übergeben werden.")
|
||||
print("👉 Beispiel: python3 uninstall_rpc.py vvow_einweisungen")
|
||||
sys.exit(1)
|
||||
|
||||
module_name = sys.argv[1]
|
||||
|
||||
# ----------------------
|
||||
# AUTHENTIFIZIERUNG
|
||||
# ----------------------
|
||||
common = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/common")
|
||||
uid = common.authenticate(db, username, password, {})
|
||||
|
||||
if not uid:
|
||||
print("❌ Anmeldung fehlgeschlagen. Bitte Zugangsdaten prüfen.")
|
||||
sys.exit(1)
|
||||
|
||||
# ----------------------
|
||||
# MODUL SUCHEN & DEINSTALLIEREN
|
||||
# ----------------------
|
||||
models = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/object")
|
||||
|
||||
ids = models.execute_kw(db, uid, password,
|
||||
'ir.module.module', 'search',
|
||||
[[['name', '=', module_name], ['state', '=', 'installed']]],
|
||||
{'limit': 1}
|
||||
)
|
||||
|
||||
if ids:
|
||||
print(f"📦 Deinstalliere Modul: {module_name}")
|
||||
models.execute_kw(db, uid, password,
|
||||
'ir.module.module', 'button_immediate_uninstall',
|
||||
[ids]
|
||||
)
|
||||
print("✅ Deinstallation abgeschlossen.")
|
||||
else:
|
||||
print(f"ℹ️ Modul '{module_name}' ist nicht installiert oder nicht vorhanden.")
|
||||
|
|
@ -2,7 +2,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
|||
access_ows_machine_access_user,ows.machine.access,model_ows_machine_access,base.group_user,1,1,1,1
|
||||
access_ows_machine_user,ows.machine,model_ows_machine,base.group_user,1,1,1,1
|
||||
access_ows_machine_product_user,ows.machine.product,model_ows_machine_product,base.group_user,1,1,1,1
|
||||
access_ows_machine_training_user,access_ows_machine_training_user,model_ows_machine_training,base.group_user,1,1,1,1
|
||||
access_ows_machine_area,ows.machine.area,model_ows_machine_area,base.group_user,1,1,1,1
|
||||
access_ows_user,ows.user,model_ows_user,base.group_user,1,1,1,1
|
||||
access_ows_machine_training,ows.machine.training,model_ows_machine_training,base.group_user,1,1,1,1
|
||||
access_ows_machine_training_user,ows.machine.training,model_ows_machine_training,base.group_user,1,1,1,1
|
||||
|
||||
|
|
|
|||
|
File diff suppressed because it is too large
Load Diff
|
|
@ -1,132 +0,0 @@
|
|||
/* This file is based on the original code from the OrderWidget in Odoo Point of Sale module (screen.js).
|
||||
* It has been modified to create a sidebar that displays machine access information for the selected customer.*/
|
||||
|
||||
odoo.define('open_workshop.machine_access_sidebar', function (require) {
|
||||
"use strict";
|
||||
|
||||
const DUMMY_PARTNER = {
|
||||
id: 1,
|
||||
name: "AAAA Max Mustermann",
|
||||
security_briefing: false,
|
||||
security_id: null,
|
||||
create_date: null,
|
||||
};
|
||||
|
||||
var rpc = require('web.rpc');
|
||||
var screens = require('point_of_sale.screens');
|
||||
var chrome = require('point_of_sale.chrome');
|
||||
var core = require('web.core');
|
||||
var QWeb = core.qweb;
|
||||
|
||||
|
||||
|
||||
var MachineAccessSidebar = screens.ScreenWidget.extend({
|
||||
template: 'MachineAccessSidebar',
|
||||
|
||||
init: function(parent, options) {
|
||||
this._super(parent, options);
|
||||
this.partner = null;
|
||||
this.pos.bind('change:selectedOrder', this.bind_order_events, this);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this._super();
|
||||
this.render_access();
|
||||
},
|
||||
|
||||
update_machine_access: function(partner) {
|
||||
console.log("🔁 Sidebar aktualisiert Maschinenfreigaben für Partner:", partner);
|
||||
this.partner = partner;
|
||||
this.render_access();
|
||||
},
|
||||
|
||||
render_access: function() {
|
||||
var self = this;
|
||||
var partner = this.partner || DUMMY_PARTNER;
|
||||
|
||||
|
||||
|
||||
rpc.query({
|
||||
model: 'ows.machine',
|
||||
method: 'get_access_list_grouped',
|
||||
args: [partner.id],
|
||||
}).then(function (result) {
|
||||
partner.create_date = partner.create_date && partner.create_date.substring(0, 10);
|
||||
var html = QWeb.render('PartnerMachineAccessList', {
|
||||
areas: result || [],
|
||||
partner: partner,
|
||||
});
|
||||
self.$('.access-content').html(html);
|
||||
});
|
||||
},
|
||||
|
||||
bind_order_events: function () {
|
||||
var order = this.pos.get_order();
|
||||
if (!order) return;
|
||||
|
||||
/*order.unbind('change:client', this);
|
||||
order.bind('change:client', this, function () {
|
||||
this.update_machine_access(order.get_client());
|
||||
});*/
|
||||
|
||||
this.update_machine_access(order.get_client());
|
||||
}
|
||||
});
|
||||
|
||||
// Sidebar aktualisieren bei Klick im ClientListScreenWidget (Vorschau)
|
||||
screens.ClientListScreenWidget.include({
|
||||
show: function () {
|
||||
this._super();
|
||||
$('.order-selector').hide(); // ← wird hier versteckt
|
||||
},
|
||||
hide: function () {
|
||||
this._super();
|
||||
$('.order-selector').show(); // ← beim Verlassen wieder zeigen
|
||||
},
|
||||
display_client_details: function (visibility, partner, clickpos) {
|
||||
this._super(visibility, partner, clickpos);
|
||||
|
||||
try {
|
||||
if (partner && typeof partner === 'object' && 'id' in partner) {
|
||||
var sidebar = this.pos.chrome.sidebar_widget;
|
||||
if (sidebar) {
|
||||
console.log("👤 ClientListScreen: Vorschau für", partner.name);
|
||||
sidebar.update_machine_access(partner);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("⚠️ Fehler beim Update der Sidebar nach Kundenauswahl:", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chrome.Chrome.include({
|
||||
build_widgets: function () {
|
||||
this._super();
|
||||
|
||||
var sidebar = new MachineAccessSidebar(this, {});
|
||||
this.sidebar_widget = sidebar;
|
||||
sidebar.appendTo(this.$el);
|
||||
|
||||
//this.pos.bind('change:selectedOrder', sidebar.bind_order_events, sidebar);
|
||||
|
||||
if (this.pos.get_order()) {
|
||||
sidebar.bind_order_events();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
odoo.define('open_workshop.models', function (require) {
|
||||
"use strict";
|
||||
var models = require('point_of_sale.models');
|
||||
var field_utils = require('web.field_utils');
|
||||
models.load_fields('res.partner', 'create_date');
|
||||
models.load_fields('res.partner', 'birthday');
|
||||
models.load_fields('res.partner', 'security_briefing');
|
||||
models.load_fields('res.partner', 'security_id');
|
||||
models.load_fields('res.partner', 'rfid_card');
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<!-- overwrite client details (view) -->
|
||||
<t t-extend="ClientDetails">
|
||||
<t t-jquery=".client-details-left" t-operation="replace">
|
||||
<div class='client-details-left'>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Address</span>
|
||||
<t t-if='partner.address'>
|
||||
<span class='detail client-address'>
|
||||
<t t-esc='partner.address' />
|
||||
</span>
|
||||
</t>
|
||||
<t t-if='!partner.address'>
|
||||
<span class='detail client-address empty'>N/A</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Email</span>
|
||||
<t t-if='partner.email'>
|
||||
<span class='detail client-email'>
|
||||
<t t-esc='partner.email' />
|
||||
</span>
|
||||
</t>
|
||||
<t t-if='!partner.email'>
|
||||
<span class='detail client-email empty'>N/A</span>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Geburtstag</span>
|
||||
<t t-if='partner.birthday'>
|
||||
<span class='detail client-vvow_birthday'>
|
||||
<t t-esc='partner.birthday' />
|
||||
</span>
|
||||
</t>
|
||||
<t t-if='!partner.birthday'>
|
||||
<span class='detail client-vvow_birthday empty'>N/A</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Phone</span>
|
||||
<t t-if='partner.phone'>
|
||||
<span class='detail client-phone'>
|
||||
<t t-esc='partner.phone' />
|
||||
</span>
|
||||
</t>
|
||||
<t t-if='!partner.phone'>
|
||||
<span class='detail client-phone empty'>N/A</span>
|
||||
</t>
|
||||
</div>
|
||||
<div t-attf-class='client-detail #{widget.pos.pricelists.length <= 1 ? "oe_hidden" : ""}'>
|
||||
<span class='label'>Pricelist</span>
|
||||
<t t-if='partner.property_product_pricelist'>
|
||||
<span class='detail property_product_pricelist'>
|
||||
<t t-esc='partner.property_product_pricelist[1]'/>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if='!partner.property_product_pricelist'>
|
||||
<span class='detail property_product_pricelist empty'>N/A</span>
|
||||
</t>
|
||||
</div>
|
||||
<div class='client-detail'>
|
||||
<t t-if='!partner.security_briefing'><span class='detail client-details-vvow_sec_briefing_error'>Haftungsausschluss prüfen!</span></t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</t>
|
||||
<t t-jquery=".client-details-right" t-operation="replace"/>
|
||||
|
||||
</t>
|
||||
</templates>
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<!-- overwrite client details (view) -->
|
||||
<t t-extend="ClientDetailsEdit">
|
||||
<t t-jquery=".client-details-left" t-operation="replace">
|
||||
<div class='client-details-left'>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Street</span>
|
||||
<input class='detail client-address-street' name='street' t-att-value='partner.street || ""' placeholder='Street'></input>
|
||||
</div>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Postcode</span>
|
||||
<input class='detail client-address-zip' name='zip' t-att-value='partner.zip || ""' placeholder='ZIP'></input>
|
||||
</div>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>City</span>
|
||||
<input class='detail client-address-city' name='city' t-att-value='partner.city || ""' placeholder='City'></input>
|
||||
</div>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Country</span>
|
||||
<select class='detail client-address-country needsclick' name='country_id'>
|
||||
<option value=''>None</option>
|
||||
<t t-foreach='widget.pos.countries' t-as='country'>
|
||||
<option t-att-value='country.id' t-att-selected="partner.country_id ? ((country.id === partner.country_id[0]) ? true : undefined) : undefined">
|
||||
<t t-esc='country.name'/>
|
||||
</option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Email</span>
|
||||
<input class='detail client-email' name='email' type='email' t-att-value='partner.email || ""'></input>
|
||||
</div>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Phone</span>
|
||||
<input class='detail client-phone' name='phone' type='tel' t-att-value='partner.phone || ""'></input>
|
||||
</div>
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Geburtstag</span>
|
||||
<input class='detail client-birthday' name='birthday' type='date' t-att-value='partner.birthday || ""'></input>
|
||||
</div>
|
||||
<div t-attf-class='client-detail #{widget.pos.pricelists.length <= 1 ? "oe_hidden" : ""}'>
|
||||
<span class='label'>Pricelist</span>
|
||||
<select class='detail needsclick' name='property_product_pricelist'>
|
||||
<t t-foreach='widget.pos.pricelists' t-as='pricelist'>
|
||||
<option t-att-value='pricelist.id' t-att-selected="partner.property_product_pricelist ? (pricelist.id === partner.property_product_pricelist[0] ? true : undefined) : undefined">
|
||||
<t t-esc='pricelist.display_name'/>
|
||||
</option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class='client-detail'>
|
||||
<span class='label'>Haftungsauschschluß</span>
|
||||
<select class='detail client-vvow_security_briefing-states needsclick' name='security_briefing'>
|
||||
<option value='true' t-att-selected="partner.security_briefing ? true : undefined">Ja</option>
|
||||
<option value='' t-att-selected="!partner.security_briefing ? true: undefined">Nein</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-jquery=".client-details-right" t-operation="replace"/>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="MachineAccessSidebar" owl="1">
|
||||
<div class="machine-access-sidebar">
|
||||
<div class="access-content">
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="PartnerMachineAccessList">
|
||||
<div class="client-details-grid">
|
||||
<div class="client-details-header">
|
||||
<ul>
|
||||
<li><span class="client-details-label">Einweisungen</span></li>
|
||||
</ul>
|
||||
|
||||
<div class="client-details-area" t-att-style="'border: solid 3px #ffffff; margin: 5px;'">
|
||||
<ul>
|
||||
<li class="client-detail">
|
||||
<span class="detail client-details-vvow_briefing">✅</span>
|
||||
<span class="briefinglabel">Werkstatt</span>
|
||||
</li>
|
||||
|
||||
<li class="client-detail">
|
||||
<t t-if="!partner.security_briefing">
|
||||
<span class="detail client-details-vvow_briefing_error">❌</span>
|
||||
</t>
|
||||
<t t-if="partner.security_briefing">
|
||||
<span class="detail client-details-vvow_briefing">✅</span>
|
||||
</t>
|
||||
<span class="briefinglabel">Haftungsausschluss</span>
|
||||
</li>
|
||||
|
||||
|
||||
<t t-if="!partner.security_briefing">
|
||||
<li class="client-detail">
|
||||
<ul class="subpoints"><span class="detail client-details-vvow_sec_briefing_error">‼️Bitte Prüfen‼️</span></ul>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
|
||||
<t t-if="partner.security_briefing">
|
||||
<ul class="subpoints">
|
||||
<li class="client-detail">
|
||||
<span class="label">Id:</span>
|
||||
<t t-if="partner.security_id">
|
||||
<span class="detail client-details-vvow_security_id"><t t-esc="partner.security_id"/></span>
|
||||
</t>
|
||||
<t t-if="!partner.security_id">
|
||||
<span class="detail client-details-vvow_security_id">N/A</span>
|
||||
</t>
|
||||
</li>
|
||||
<li class="client-detail">
|
||||
<span class="label">Erstellt:</span>
|
||||
<t t-if="partner.create_date">
|
||||
<span class="detail client-details-vvow_security_id"><t t-esc="partner.create_date"/></span>
|
||||
</t>
|
||||
<t t-if="!partner.create_date">
|
||||
<span class="detail client-vvow_birthday">N/A</span>
|
||||
</t>
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<t t-foreach="areas" t-as="area">
|
||||
<div class="client-details-area" t-att-style="'border: solid 3px ' + area.color_hex + '; margin: 5px;'">
|
||||
<ul>
|
||||
<t t-foreach="area.machines" t-as="machine">
|
||||
<li class="client-detail">
|
||||
<t t-if="!machine.has_access">
|
||||
<span class="detail client-details-vvow_briefing_error">❌</span>
|
||||
</t>
|
||||
<t t-if="machine.has_access">
|
||||
<span class="detail client-details-vvow_briefing">✅</span>
|
||||
</t>
|
||||
<span class="briefinglabel"><t t-esc="machine.name"/></span>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<!-- Test replace logo -->
|
||||
<t t-extend="Chrome">
|
||||
<t t-jquery=".pos-branding" t-operation="replace">
|
||||
<img style="max-height:48px; max-width: 100%; width:auto" src="/web/binary/company_logo" alt="HOBBYHIMMEL"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!-- Remove old order-selector container -->
|
||||
<t t-extend="Chrome">
|
||||
<t t-jquery=".placeholder-OrderSelectorWidget" t-operation="replace">
|
||||
</t>
|
||||
</t>
|
||||
<!-- insert order-selector container in new position -->
|
||||
<t t-extend="Chrome">
|
||||
<t t-jquery=".pos" t-operation="prepend">
|
||||
<div class="placeholder-OrderSelectorWidget"></div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!-- completly overwrite OrderSelectorWidget -->
|
||||
<t t-name="OrderSelectorWidget">
|
||||
<div class="order-selector" >
|
||||
<div class="new-order-button">
|
||||
<span class="order-button square neworder-button">
|
||||
<i class='fa fa-plus' role="img" aria-label="New order" title="New order"/>
|
||||
</span>
|
||||
<span class="order-button square deleteorder-button">
|
||||
<i class='fa fa-minus' role="img" aria-label="Delete order" title="Delete order"/>
|
||||
</span>
|
||||
</div>
|
||||
<span class="orders touch-scrollable">
|
||||
<t t-foreach="widget.pos.get_order_list()" t-as="order">
|
||||
<t t-if="order === widget.pos.get_order()">
|
||||
<span class="order-button select-order selected" t-att-title="order.sequence_number" t-att-data-uid="order.uid">
|
||||
<span class="order-time">
|
||||
<t t-esc="moment(order.creation_date).format('HH:mm')"/>
|
||||
</span>
|
||||
<span class="order-customer">
|
||||
<t t-if="order.get_client()">
|
||||
<t t-esc="order.get_client().name" />
|
||||
</t>
|
||||
<t t-if="!order.get_client()">
|
||||
?
|
||||
</t>
|
||||
</span>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="order !== widget.pos.get_order()">
|
||||
<span class="order-button select-order" t-att-title="order.sequence_number" t-att-data-uid="order.uid">
|
||||
<span class="order-time">
|
||||
<t t-esc="moment(order.creation_date).format('HH:mm')"/>
|
||||
</span>
|
||||
<span class="order-customer">
|
||||
<t t-if="order.get_client()">
|
||||
<t t-esc="order.get_client().name" />
|
||||
</t>
|
||||
<t t-if="!order.get_client()">
|
||||
?
|
||||
</t>
|
||||
</span>
|
||||
</span>
|
||||
</t>
|
||||
</t>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
|
@ -1 +1,2 @@
|
|||
from . import test_res_partner
|
||||
from . import test_access_rights
|
||||
|
|
|
|||
74
tests/test_access_rights.py
Normal file
74
tests/test_access_rights.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
'''
|
||||
/odoo_env/src/odoo/odoo-bin \
|
||||
-d hh16-test \
|
||||
--data-dir=/env/filestore/ \
|
||||
--addons-path=/odoo_env/src/odoo/addons,/odoo_env/src/odoo/odoo/addons,/odoo_env/src/openupgrade,/odoo_env/src/OCA/web,/odoo_env/src/OCA/server-tools,/odoo_env/src/vvow \
|
||||
--test-enable \
|
||||
--stop-after-init \
|
||||
--test-tags open_workshop \
|
||||
--log-level=debug \
|
||||
--http-port=9070 \
|
||||
--db_host=db \
|
||||
--db_user=odoo \
|
||||
--db_password=odoo
|
||||
'''
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.tests import tagged
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@tagged('post_install', 'open_workshop', 'access_rights')
|
||||
class TestOpenWorkshopAccessRights(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.group_user = self.env.ref('base.group_user')
|
||||
self.test_user = self.env['res.users'].create({
|
||||
'name': 'Access Test User',
|
||||
'login': 'access_test_user',
|
||||
'groups_id': [(6, 0, [self.group_user.id])],
|
||||
})
|
||||
self.models_to_test = [
|
||||
'ows.machine',
|
||||
'ows.machine.access',
|
||||
'ows.machine.product',
|
||||
'ows.machine.training',
|
||||
'ows.machine.area',
|
||||
'ows.user',
|
||||
]
|
||||
|
||||
def test_model_access_rights(self):
|
||||
for model_name in self.models_to_test:
|
||||
with self.subTest(model=model_name):
|
||||
model = self.env[model_name].with_user(self.test_user)
|
||||
records = model.search([], limit=1)
|
||||
|
||||
# Test Lesezugriff
|
||||
self.assertTrue(records.exists(), f"Kein Zugriff auf {model_name} oder leer")
|
||||
|
||||
# Test Schreib-, Erstell- und Löschrechte nur wenn Eintrag existiert
|
||||
if records:
|
||||
# Schreibtest
|
||||
write_fields = [f for f in records._fields if records._fields[f].type in ('char', 'text') and f != 'id']
|
||||
if write_fields:
|
||||
field = write_fields[0]
|
||||
test_value = 'Test Write'
|
||||
try:
|
||||
records.write({field: test_value})
|
||||
except Exception as e:
|
||||
self.fail(f"❌ Schreibrechte auf {model_name}.{field} verweigert: {e}")
|
||||
|
||||
# Erstellung
|
||||
try:
|
||||
new = records.copy()
|
||||
self.assertTrue(new.exists(), f"❌ Kein Erstellrecht auf {model_name}")
|
||||
except Exception as e:
|
||||
self.fail(f"❌ Erstellung in {model_name} verweigert: {e}")
|
||||
|
||||
# Löschung
|
||||
try:
|
||||
new.unlink()
|
||||
except Exception as e:
|
||||
self.fail(f"❌ Löschrechte auf {model_name} verweigert: {e}")
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
## Testausführung
|
||||
# odoo-bin -d deine_testdatenbank --test-enable --init open_workshop
|
||||
## oder:
|
||||
# /opt/odoo/odoo/odoo-bin -d hobbyhimmel --test-enable --test-tags /test_res_partner.py --stop-after-init --http-port=8070 --log-level=debug
|
||||
# /opt/odoo/odoo/odoo-bin -d hobbyhimmel --test-enable --stop-after-init --test-tags open_workshop --log-level=debug --update open_workshop --http-port=8070
|
||||
# /odoo_env/src/odoo/odoo-bin -d hh16 --test-enable --stop-after-init --test-tags open_workshop --log-level=debug --update open_workshop --stop-after-init --db_host=db --db_user=odoo --db_password=odoo
|
||||
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.tests import tagged
|
||||
|
|
|
|||
75
todo.md
75
todo.md
|
|
@ -1,3 +1,72 @@
|
|||
[ ] Help System
|
||||
[ ] Möglichkeit, Einweisungen manuell zu setzen?
|
||||
[ ]
|
||||
# TODO: Reaktivierung des Moduls "Open Workshop" in Odoo 16.0
|
||||
|
||||
## ✨ Ziel
|
||||
Die schrittweise Wiederherstellung der Funktionalität des Moduls `open_workshop` in einer nach Odoo 16.0 migrierten Instanz, basierend auf einer zuvor deaktivierten Datenbank.
|
||||
|
||||
---
|
||||
|
||||
## 🔄 1. Grundlagen sicherstellen
|
||||
- [x] Sicherstellen, dass `ows_models.py` korrekt geladen wird
|
||||
- [x] Alle Modelle müssen sich fehlerfrei installieren lassen
|
||||
- [x] Tabellen wie `ows_user`, `ows_machine`, etc. sind vorhanden und konsistent
|
||||
- [x] Relation `res.partner.ows_user_id` vorhanden
|
||||
|
||||
---
|
||||
|
||||
## 🧹 2. Datenbasierte Komponenten reaktivieren
|
||||
### 2.1 `data/demo_data.xml`
|
||||
- [ ] Beispieldaten für `ows.machine.area` und `ows.machine` erstellen
|
||||
- [ ] IDs müssen konfliktfrei mit bestehender Datenbank sein
|
||||
- [ ] Module danach neu starten: `-u open_workshop`
|
||||
|
||||
### 2.2 `security/ir.model.access.csv`
|
||||
- [x] Zugriff für alle verwendeten Modelle definieren
|
||||
- `ows.user`, `ows.machine`, `ows.machine.access`
|
||||
- Optional: `res.partner` (nur read)
|
||||
- [x] Installieren
|
||||
- [ ] testen ob Zugriff möglich ist -> tests/test_access_rights.py
|
||||
|
||||
---
|
||||
|
||||
## 🎨 3. Backend Views stufenweise aktivieren
|
||||
### 3.1 `views/menu_views.xml`
|
||||
- [ ] Menüs einbinden, ohne Abhängigkeiten
|
||||
- [ ] Test: Odoo starten & Menüs sichtbar?
|
||||
|
||||
### 3.2 `views/machine_area_views.xml`
|
||||
### 3.3 `views/machine_views.xml`
|
||||
- [ ] Baumansicht (list) zuerst aktivieren
|
||||
- [ ] Danach Form-View (form) hinzufügen
|
||||
|
||||
### 3.4 `views/res_partner_view.xml`
|
||||
- [ ] Tab "Maschinenfreigaben" aktivieren
|
||||
- [ ] Nur Felder mit klarer Modellbindung einbinden
|
||||
|
||||
---
|
||||
|
||||
## 💻 4. POS Assets & QWeb (optional)
|
||||
### 4.1 `views/assets.xml`
|
||||
- [ ] QWeb-Templates und POS JS nur aktivieren, wenn POS-Modul auch vorhanden ist
|
||||
- [ ] Kompatibilität zu JS (ES5 / `odoo.define`) prüfen
|
||||
|
||||
---
|
||||
|
||||
## 🔬 5. Tools & Debugging
|
||||
- [ ] `odoo-bin shell -d hh16` für gezielte Tests nutzen
|
||||
- [ ] Überprüfen ob Einträge in `ir.model.data` korrekt vorhanden sind
|
||||
- [ ] Logdateien auf Foreign Key oder View-Probleme prüfen
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Optional
|
||||
- [ ] `migrate_existing_partners()` über `res.partner` testen
|
||||
- [ ] Migration der alten `vvow_*` Felder validieren
|
||||
|
||||
---
|
||||
|
||||
## ⚙ Nächste Schritte
|
||||
- [ ] Schritt 2 (Demo- und Security-Dateien) zuerst
|
||||
- [ ] Schrittweise View-Dateien aktivieren
|
||||
- [ ] Modul vollständig über Backend installierbar machen
|
||||
- [ ] POS-Integration zuletzt wiederherstellen
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
<odoo>
|
||||
<data>
|
||||
<template id="assets" inherit_id="point_of_sale.assets">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/open_workshop/static/src/js/machine_access_sidebar.js"/>
|
||||
<template id="machine_access_template" name="Maschinenfreigaben Template" src="/open_workshop/static/src/xml/ows_machine_sidebar.xml"/>
|
||||
</xpath>
|
||||
<xpath expr="//link[@href='/point_of_sale/static/src/css/pos.css']" position="replace">
|
||||
<link rel="stylesheet" type="text/css" href="/open_workshop/static/src/css/pos.css"/>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<!-- machine_area_views.xml -->
|
||||
<odoo>
|
||||
<!-- Action zum Anzeigen der Bereiche -->
|
||||
<record id="action_machine_area_list" model="ir.actions.act_window">
|
||||
<field name="name">Maschinenbereiche</field>
|
||||
<field name="res_model">ows.machine.area</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!-- Menüpunkt unter Maschinen > Konfiguration -->
|
||||
<menuitem id="menu_machine_area" name="Bereiche" parent="menu_machine_config" action="open_workshop.action_machine_area_list" sequence="30"/>
|
||||
|
||||
<!-- Listenansicht -->
|
||||
<record id="view_machine_area_tree" model="ir.ui.view">
|
||||
<field name="name">ows.machine.area.tree</field>
|
||||
<field name="model">ows.machine.area</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="color_hex" widget="color_picker"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Formularansicht -->
|
||||
<record id="view_machine_area_form" model="ir.ui.view">
|
||||
<field name="name">ows.machine.area.form</field>
|
||||
<field name="model">ows.machine.area</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Maschinenbereich">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="color_hex" widget="color_picker"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<odoo>
|
||||
<!-- Tree View: Nutzungsprodukte -->
|
||||
<record id="view_machine_product_tree" model="ir.ui.view">
|
||||
<field name="name">ows.machine.product.tree</field>
|
||||
<field name="model">ows.machine.product</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="machine_id"/>
|
||||
<field name="product_id" domain="[('categ_id.name', '=', 'Maschinennutzung')]"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Tree View: Einweisungsprodukte -->
|
||||
<record id="view_machine_training_tree" model="ir.ui.view">
|
||||
<field name="name">ows.machine.training.tree</field>
|
||||
<field name="model">ows.machine.training</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="machine_id"/>
|
||||
<field name="training_id" domain="[('categ_id.name', '=', 'Einweisungen')]"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action: Nutzungsprodukte -->
|
||||
<record id="action_machine_product" model="ir.actions.act_window">
|
||||
<field name="name">Maschinen-Nutzungsprodukte</field>
|
||||
<field name="res_model">ows.machine.product</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_machine_product_tree"/>
|
||||
<field name="help" type="html">
|
||||
<p>Verwalte die Zuordnung von Maschinen zu Nutzungsprodukten.</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action: Einweisungsprodukte -->
|
||||
<record id="action_machine_training" model="ir.actions.act_window">
|
||||
<field name="name">Maschinen-Einweisungsprodukte</field>
|
||||
<field name="res_model">ows.machine.training</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_machine_training_tree"/>
|
||||
<field name="help" type="html">
|
||||
<p>Verwalte die Zuordnung von Maschinen zu Einweisungsprodukten.</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
</odoo>
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
<!-- machine_views.xml -->
|
||||
<odoo>
|
||||
<!-- Maschinen Listenansicht -->
|
||||
<record id="view_machine_tree" model="ir.ui.view">
|
||||
<field name="name">ows.machine.tree</field>
|
||||
<field name="model">ows.machine</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="area_id" widget="many2one_color"/>
|
||||
<field name="product_names"/>
|
||||
<field name="training_names"/>
|
||||
<field name="active"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Maschinen Formularansicht -->
|
||||
<record id="view_machine_form" model="ir.ui.view">
|
||||
<field name="name">ows.machine.form</field>
|
||||
<field name="model">ows.machine</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Maschine">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="area_id"/>
|
||||
<field name="description"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
|
||||
<!-- Neue -->
|
||||
<notebook>
|
||||
<page string="Nutzungsprodukte">
|
||||
<field name="product_ids" context="{'default_machine_id': active_id}">
|
||||
<tree editable="bottom">
|
||||
<field name="product_id" domain="[('categ_id.name', '=', 'Maschinennutzung')]" />
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Einweisungsprodukte">
|
||||
<field name="training_ids" context="{'default_machine_id': active_id}">
|
||||
<tree editable="bottom">
|
||||
<field name="training_id" domain="[('categ_id.name', '=', 'Einweisungen')]" />
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
<!-- menu_views.xml -->
|
||||
<odoo>
|
||||
<!-- Maschinenliste -->
|
||||
<record id="action_machine_list" model="ir.actions.act_window">
|
||||
<field name="name">Maschinen</field>
|
||||
<field name="res_model">ows.machine</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!-- Trainingsprodukt-Liste -->
|
||||
<record id="action_training_product_list" model="ir.actions.act_window">
|
||||
<field name="name">Einweisungs-Produkte</field>
|
||||
<field name="res_model">ows.machine.product</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!-- Menüstruktur -->
|
||||
<!-- Oberstes Menü -->
|
||||
<menuitem id="menu_machine_root"
|
||||
name="Maschinen"
|
||||
sequence="10"/>
|
||||
|
||||
<!-- Konfigurationsebene -->
|
||||
<menuitem id="menu_machine_config"
|
||||
name="Konfiguration"
|
||||
parent="menu_machine_root"
|
||||
sequence="10"/>
|
||||
|
||||
<!-- Menüpunkt: Maschinenliste (klickbar) -->
|
||||
<menuitem id="menu_machine_list_action"
|
||||
name="Alle Maschinen"
|
||||
parent="menu_machine_config"
|
||||
action="open_workshop.action_machine_list"
|
||||
sequence="10"/>
|
||||
|
||||
<!-- Menücontainer: Zuordnungen -->
|
||||
<menuitem id="menu_machine_list"
|
||||
name="Zuordnungen"
|
||||
parent="menu_machine_config"
|
||||
sequence="20"/>
|
||||
|
||||
<!-- Untermenü: Nutzungsprodukte -->
|
||||
<menuitem id="menu_machine_product"
|
||||
name="Nutzungsprodukte"
|
||||
parent="menu_machine_list"
|
||||
action="action_machine_product"
|
||||
sequence="10"/>
|
||||
|
||||
<!-- Untermenü: Einweisungsprodukte -->
|
||||
<menuitem id="menu_machine_training"
|
||||
name="Einweisungsprodukte"
|
||||
parent="menu_machine_list"
|
||||
action="action_machine_training"
|
||||
sequence="20"/>
|
||||
|
||||
|
||||
<!-- List & Form Views für training.product -->
|
||||
<record id="view_training_product_tree" model="ir.ui.view">
|
||||
<field name="name">ows.machine.product.tree</field>
|
||||
<field name="model">ows.machine.product</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="product_id"/>
|
||||
<field name="machine_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_training_product_form" model="ir.ui.view">
|
||||
<field name="name">ows.machine.product.form</field>
|
||||
<field name="model">ows.machine.product</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Einweisungs-Produkt">
|
||||
<group>
|
||||
<field name="product_id"/>
|
||||
<field name="machine_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
<!-- res_partner_view.xml -->
|
||||
<odoo>
|
||||
<!-- View-Erweiterung für res.partner: Tab mit HTML-Tabelle
|
||||
Der Inhalt wird in der Methode _compute_machine_access_html() generiert.
|
||||
Diese Methode wird in der Klasse res.partner definiert in der Datei models/ows_models.py.
|
||||
Die Methode wird aufgerufen, wenn das Partnerformular geöffnet wird.
|
||||
Die HTML-Tabelle wird in der Variable machine_access_html gespeichert.
|
||||
Die Variable wird in der View angezeigt.
|
||||
-->
|
||||
<record id="view_partner_form_inherit_open_workshop_html" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.ows.machine.access.html</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<notebook position="inside">
|
||||
<page string="HOBBYHIMMEL Einweisungen">
|
||||
<field name="machine_access_html" readonly="1" widget="html"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Teil 1: Maschinenfreigaben-Tabelle -->
|
||||
<record id="view_partner_form_inherit_open_workshop" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.ows.machine.access</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<notebook position="inside">
|
||||
<page string="Einweisungen (Liste)">
|
||||
<field name="machine_access_ids">
|
||||
<tree>
|
||||
<field name="machine_id"/>
|
||||
<field name="date_granted"/>
|
||||
<field name="date_expiry"/>
|
||||
<field name="granted_by_pos"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Teil 2: HOBBYHIMMEL Basis (ows_user_id Felder) -->
|
||||
<record id="view_partner_form_inherit_ows_user" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.ows.user</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
|
||||
<!-- Geburtstag direkt unter USt-ID -->
|
||||
<xpath expr="//field[@name='vat']" position="after">
|
||||
<field name="birthday"/>
|
||||
</xpath>
|
||||
|
||||
<!-- Eigene Seite "Basis" nach der Verkaufsseite -->
|
||||
<xpath expr="//page[@name='sales_purchases']" position="after">
|
||||
<page name="ows_basic" string="HOBBYHIMMEL Basis">
|
||||
<group name="container_row_2">
|
||||
<group string="Sicherheit">
|
||||
<field name="security_briefing"/>
|
||||
<field name="security_id"/>
|
||||
</group>
|
||||
<group string="Zugang">
|
||||
<field name="rfid_card"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
<record id="contacts.action_contacts" model="ir.actions.act_window">
|
||||
<field name="view_mode">tree,kanban,form,activity</field>
|
||||
</record>
|
||||
<record id="contacts.action_contacts_view_kanban" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="1"/>
|
||||
</record>
|
||||
<record id="contacts.action_contacts_view_tree" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="0"/>
|
||||
<!--tree default_order="create_date desc"/-->
|
||||
</record>
|
||||
<!-- Action to set default view to list view for Contacts
|
||||
<record id="contacts.action_contacts" model="ir.actions.act_window">
|
||||
<field name="name">Contacts</field>
|
||||
<field name="res_model">res.partner</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="view_id" ref="base.view_partner_tree"/>
|
||||
</record>
|
||||
-->
|
||||
<record id="ows_userList_inherit" model="ir.ui.view">
|
||||
<field name="name">res.partner.ows.tree</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='vat']" position="after">
|
||||
<field name="create_date" optional="show"/>
|
||||
<field name="security_briefing" optional="show"/>
|
||||
<field name="security_id" optional="show"/>
|
||||
<field name="rfid_card" optional="show"/>
|
||||
<field name="category_id" widget="many2many_tags"/>
|
||||
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='vat']" position="replace">
|
||||
<field name="vat" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='email']" position="replace">
|
||||
<field name="email" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='phone']" position="replace">
|
||||
<field name="phone" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='state_id']" position="replace">
|
||||
<field name="state_id" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='country_id']" position="replace">
|
||||
<field name="country_id" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_partner_form_inherit" model="ir.ui.view">
|
||||
<field name="name">res.partner.form.inherit.default_person</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_type" position="attributes">
|
||||
<attribute name="default">person</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Loading…
Reference in New Issue
Block a user