🎉 Initialize module repository

This commit is contained in:
Marc Wempe
2026-04-03 23:08:58 +02:00
commit 5d4a312621
13 changed files with 2896 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
__pycache__/
*.py[cod]
.DS_Store
.pytest_cache/
.ruff_cache/
*.log
*.swp
*~

2
__init__.py Normal file
View File

@@ -0,0 +1,2 @@
from . import models
from . import report

30
__manifest__.py Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "MVD TCG MTG Deck",
"summary": "Magic-specific deckbuilding rules, analytics, and deck views",
"version": "19.0.10.5.0",
"description": """
Magic: The Gathering deckbuilding extension for the MVD TCG suite.
This module turns the neutral deck layer into an MTG-focused deckbuilder with:
- Command Zone, Mainboard, Sideboard, and Maybeboard workflows
- Commander and format-aware rule hints
- mana cost, legality, and type signals on deck lines
- analysis panels such as mana curve, role coverage, type mix, and color pips
- MTG-specific report additions and deck views
It is the main gameplay-oriented deckbuilding module for MTG before commerce
and collection features are added.
""",
"category": "Tools",
"author": "Mantjeverse Digital",
"license": "LGPL-3",
"depends": ["mvd_tcg_deck", "mvd_tcg_mtg"],
"data": [
"views/menu_views.xml",
"views/mvd_tcg_mtg_deck_views.xml",
"views/mvd_tcg_mtg_card_views.xml",
"report/mvd_tcg_mtg_deck_report_templates.xml",
],
"application": False,
"installable": True,
}

726
i18n/de.po Normal file
View File

@@ -0,0 +1,726 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * mvd_tcg_mtg_deck
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 19.0+e-20260324\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-03 05:05+0000\n"
"PO-Revision-Date: 2026-04-03 05:05+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Language: de\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "%(count)s line(s) are banned or not legal in %(format)s."
msgstr "%(count)s Zeile(n) sind in %(format)s gebannt oder nicht legal."
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "%(count)s line(s) exceed the current commander color identity."
msgstr "%(count)s Zeile(n) überschreiten die aktuelle Commander-Farbidentität."
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid ""
"%(count)s line(s) violate singleton rules. Basic lands and cards with "
"explicit unlimited-copy text are ignored."
msgstr ""
"%(count)s Zeile(n) verletzen die Singleton-Regeln. Standardländer und Karten "
"mit explizit unbegrenzter Kopienanzahl werden ignoriert."
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "%(count)s restricted line(s) exceed one copy in %(format)s."
msgstr "%(count)s eingeschränkte Zeile(n) überschreiten ein Exemplar in %(format)s."
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "<span class=\"o_mvd_tcg_report_muted\"> · </span>"
msgstr "<span class=\"o_mvd_tcg_report_muted\"> · </span>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_form_mtg_inherit
msgid "<span class=\"o_stat_text\">Command</span>"
msgstr "<span class=\"o_stat_text\">Command</span>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_form_mtg_inherit
msgid "<span class=\"o_stat_text\">Mainboard</span>"
msgstr "<span class=\"o_stat_text\">Mainboard</span>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_form_mtg_inherit
msgid "<span class=\"o_stat_text\">Maybeboard</span>"
msgstr "<span class=\"o_stat_text\">Maybeboard</span>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_form_mtg_inherit
msgid "<span class=\"o_stat_text\">Sideboard</span>"
msgstr "<span class=\"o_stat_text\">Sideboard</span>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "<strong class=\"me-1\">Cards</strong>"
msgstr "<strong class=\"me-1\">Karten</strong>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "<strong>Average Mana Value:</strong>"
msgstr "<strong>Durchschnittlicher Manawert:</strong>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "<strong>Commander:</strong>"
msgstr "<strong>Commander:</strong>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "<strong>Deck Size:</strong>"
msgstr "<strong>Deckgröße:</strong>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "<strong>Format:</strong>"
msgstr "<strong>Format:</strong>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "<strong>Identity:</strong>"
msgstr "<strong>Identität:</strong>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "<strong>Rule Warnings:</strong>"
msgstr "<strong>Regelhinweise:</strong>"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Add Card"
msgstr "Karte hinzufügen"
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "Add cards to see the mana curve."
msgstr "Füge Karten hinzu, um die Manakurve zu sehen."
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "Add cards to see the type composition."
msgstr "Füge Karten hinzu, um die Typverteilung zu sehen."
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_mtg_card_view_form_deck_inherit
msgid "Add to Deck"
msgstr "Zum Deck hinzufügen"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Analysis"
msgstr "Analyse"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Average Mana Value"
msgstr "Durchschnittlicher Manawert"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck.selection__mvd_tcg_deck_line__mtg_legality_status__banned
msgid "Banned"
msgstr "Gebannt"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_form_mtg_inherit
msgid "Card Snapshot"
msgstr "Karten-Snapshot"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_collector_number
msgid "Collector Number"
msgstr "Sammlernummer"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_color_identity_ids
msgid "Color Identity"
msgstr "Farbidentität"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "Color Identity &amp; Curve"
msgstr "Farbidentität &amp; Kurve"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_color_identity_name
msgid "Color Identity Name"
msgstr "Name der Farbidentität"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Color Pips"
msgstr "Farb-Pips"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "Color identity not available"
msgstr "Farbidentität nicht verfügbar"
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "Colored mana symbols will appear here."
msgstr "Farbige Manasymbole erscheinen hier."
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_game.py:0
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Command Zone"
msgstr "Command Zone"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_command_zone_line_ids
msgid "Command Zone Cards"
msgstr "Command-Zone-Karten"
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "Command Zone should contain exactly %(count)s card(s)."
msgstr "Die Command Zone muss genau %(count)s Karte(n) enthalten."
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "Commander Snapshot"
msgstr "Commander-Snapshot"
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck_line.py:0
msgid ""
"Commander- and Brawl-style decks can include only one copy of the same card "
"across all styles and printings. Moving this line would conflict with: "
"%(cards)s"
msgstr ""
"Commander- und Brawl-Decks dürfen dieselbe Karte über alle Varianten und "
"Drucke hinweg nur einmal enthalten. Das Verschieben dieser Zeile würde "
"kollidieren mit: %(cards)s"
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid ""
"Commander- and Brawl-style decks can include only one copy of the same card "
"across all styles and printings. This card conflicts with: %(cards)s"
msgstr ""
"Commander- und Brawl-Decks dürfen dieselbe Karte über alle Varianten und "
"Drucke hinweg nur einmal enthalten. Diese Karte kollidiert mit: %(cards)s"
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid ""
"Current Command Zone cards are not valid commander choices for the selected "
"format."
msgstr "Die aktuellen Command-Zone-Karten sind für das gewählte Format keine gültigen Commander."
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Deck Metrics"
msgstr "Deck-Metriken"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Deck Snapshot"
msgstr "Deck-Snapshot"
#. module: mvd_tcg_mtg_deck
#: model:ir.actions.act_window,name:mvd_tcg_mtg_deck.mvd_tcg_mtg_deck_action
#: model:ir.ui.menu,name:mvd_tcg_mtg_deck.mvd_tcg_mtg_decks_menu
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_mtg_card_view_form_deck_inherit
msgid "Decks"
msgstr "Decks"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__display_name
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__display_name
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_game__display_name
msgid "Display Name"
msgstr "Anzeigename"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_format_id
msgid "Format"
msgstr "Format"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "Format &amp; Commander"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__id
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__id
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_game__id
msgid "ID"
msgstr "ID"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_search_mtg_inherit
msgid "Illegal"
msgstr "Illegal"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__is_mtg_deck
msgid "Is Mtg Deck"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_search_mtg_inherit
msgid "Issues"
msgstr "Probleme"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck.selection__mvd_tcg_deck_line__mtg_legality_status__legal
msgid "Legal"
msgstr "Legal"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Mainboard"
msgstr "Mainboard"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_mainboard_line_ids
msgid "Mainboard Cards"
msgstr "Mainboard-Karten"
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "Mainboard currently has %(current)s cards, expected %(expected)s."
msgstr "Das Mainboard enthält aktuell %(current)s Karten, erwartet werden %(expected)s."
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_mana_cost
msgid "Mana Cost"
msgstr "Manakosten"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Mana Curve"
msgstr "Manakurve"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_mana_value
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_search_mtg_inherit
msgid "Mana Value"
msgstr "Manawert"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Manage Lines"
msgstr "Zeilen verwalten"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Maybeboard"
msgstr "Maybeboard"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_maybeboard_line_ids
msgid "Maybeboard Cards"
msgstr "Maybeboard-Karten"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_artifact_count
msgid "Artifacts"
msgstr "Artefakte"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_average_mana_value
msgid "Average Mana Value"
msgstr "Durchschnittlicher Manawert"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_color_identity_ok
msgid "Color Identity OK"
msgstr "Farbidentität OK"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_color_identity_signature
msgid "Color Identity Signature"
msgstr "Farbidentitäts-Signatur"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_color_identity_violation
msgid "Color Identity Violation"
msgstr "Verstoß gegen Farbidentität"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_color_pip_breakdown_html
msgid "Color Pips"
msgstr "Farb-Pips"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_command_zone_board_id
msgid "Command Zone Board"
msgstr "Command-Zone-Board"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_command_zone_count
msgid "Command Zone Count"
msgstr "Anzahl Command Zone"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_command_zone_size_ok
msgid "Command Zone Size OK"
msgstr "Größe der Command Zone OK"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_commander_card_id
msgid "Commander"
msgstr "Commander"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_commander_eligibility_ok
msgid "Commander Eligibility OK"
msgstr "Commander-Eignung OK"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_commander_image
msgid "Commander Image"
msgstr "Commander-Bild"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_creature_count
msgid "Creatures"
msgstr "Kreaturen"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_duplicate_card_count
msgid "Duplicate Cards"
msgstr "Doppelte Karten"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_enchantment_count
msgid "Enchantments"
msgstr "Verzauberungen"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_expected_command_zone_size
msgid "Expected Command Zone Size"
msgstr "Erwartete Größe der Command Zone"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_expected_mainboard_size
msgid "Expected Mainboard Size"
msgstr "Erwartete Mainboard-Größe"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_expected_sideboard_size
msgid "Expected Sideboard Size"
msgstr "Erwartete Sideboard-Größe"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_illegal_card_count
msgid "Illegal Cards"
msgstr "Illegale Karten"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_instant_count
msgid "Instants"
msgstr "Spontanzauber"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_issue_count
msgid "Issue Count"
msgstr "Anzahl Probleme"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_issue_line_count
msgid "Issue Lines"
msgstr "Problemzeilen"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_land_count
msgid "Lands"
msgstr "Länder"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_legality_ok
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_legality_ok
msgid "Legality OK"
msgstr "Legalität OK"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_legality_status
msgid "Legality Status"
msgstr "Legalitätsstatus"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_mainboard_board_id
msgid "Mainboard"
msgstr "Mainboard"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_mainboard_count
msgid "Mainboard Count"
msgstr "Anzahl Mainboard"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_mainboard_size_ok
msgid "Mainboard Size OK"
msgstr "Mainboard-Größe OK"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_mana_curve_html
msgid "Mana Curve"
msgstr "Manakurve"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_maybeboard_board_id
msgid "Maybeboard"
msgstr "Maybeboard"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_maybeboard_count
msgid "Maybeboard Count"
msgstr "Anzahl Maybeboard"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_off_color_card_count
msgid "Off-Color Cards"
msgstr "Karten außerhalb der Farbidentität"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_planeswalker_count
msgid "Planeswalkers"
msgstr "Planeswalker"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_primary_card_type_id
msgid "Primary Card Type"
msgstr "Primärer Kartentyp"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_restricted_card_count
msgid "Restricted Cards"
msgstr "Eingeschränkte Karten"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_role_breakdown_html
msgid "Role Breakdown"
msgstr "Rollenübersicht"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_role_coverage_ratio
msgid "Role Coverage"
msgstr "Rollenabdeckung"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_rule_summary
msgid "Rule Summary"
msgstr "Regelzusammenfassung"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_rule_warning_count
msgid "Rule Warnings"
msgstr "Regelhinweise"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_sideboard_board_id
msgid "Sideboard"
msgstr "Sideboard"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_sideboard_count
msgid "Sideboard Count"
msgstr "Anzahl Sideboard"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_sideboard_size_ok
msgid "Sideboard Size OK"
msgstr "Sideboard-Größe OK"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_singleton_ok
msgid "Singleton OK"
msgstr "Singleton OK"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_singleton_violation
msgid "Singleton Violation"
msgstr "Singleton-Verstoß"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_sorcery_count
msgid "Sorceries"
msgstr "Hexereien"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_tagged_line_count
msgid "Tagged Cards"
msgstr "Getaggte Karten"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_type_breakdown_html
msgid "Type Composition"
msgstr "Typverteilung"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_untagged_line_count
msgid "Untagged Cards"
msgstr "Ungetaggte Karten"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "No MTG rule notes are currently available."
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "No commander detected"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "No format selected"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck.selection__mvd_tcg_deck_line__mtg_legality_status__not_legal
msgid "Not Legal"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_search_mtg_inherit
msgid "Off Color"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck.selection__mvd_tcg_deck_line__mtg_legality_status__restricted
msgid "Restricted"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Role Breakdown"
msgstr "Rollenverteilung"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Role Coverage %"
msgstr "Rollenabdeckung %"
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "Rule Notes"
msgstr ""
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "Run role analysis or tag cards to see role coverage."
msgstr "Führe eine Rollen-Analyse aus oder tagge Karten, um die Rollenabdeckung zu sehen."
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_set_id
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_search_mtg_inherit
msgid "Set"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Sideboard"
msgstr "Sideboard"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck__mtg_sideboard_line_ids
msgid "Sideboard Cards"
msgstr "Sideboard-Karten"
#. module: mvd_tcg_mtg_deck
#. odoo-python
#: code:addons/mvd_tcg_mtg_deck/models/mvd_tcg_deck.py:0
msgid "Sideboard currently has %(current)s cards, maximum is %(expected)s."
msgstr "Das Sideboard enthält aktuell %(current)s Karten, maximal erlaubt sind %(expected)s."
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_search_mtg_inherit
msgid "Singleton"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model:ir.model,name:mvd_tcg_mtg_deck.model_mvd_tcg_deck
msgid "TCG Deck"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model:ir.model,name:mvd_tcg_mtg_deck.model_mvd_tcg_deck_line
msgid "TCG Deck Line"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model:ir.model,name:mvd_tcg_mtg_deck.model_mvd_tcg_game
msgid "TCG Game"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Tagged Cards"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_line_view_search_mtg_inherit
msgid "Type"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "Type Composition"
msgstr "Typverteilung"
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields,field_description:mvd_tcg_mtg_deck.field_mvd_tcg_deck_line__mtg_type_line
msgid "Type Line"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model:ir.model.fields.selection,name:mvd_tcg_mtg_deck.selection__mvd_tcg_deck_line__mtg_legality_status__unknown
msgid "Unknown"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.mvd_tcg_deck_view_form_mtg_inherit
msgid "Untagged Cards"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "command zone"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid ""
"mainboard\n"
" <span class=\"o_mvd_tcg_report_muted\"> · </span>"
msgstr ""
#. module: mvd_tcg_mtg_deck
#: model_terms:ir.ui.view,arch_db:mvd_tcg_mtg_deck.report_mvd_tcg_deck_document_mtg_inherit
msgid "sideboard"
msgstr ""

3
models/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
from . import mvd_tcg_game
from . import mvd_tcg_deck
from . import mvd_tcg_deck_line

1065
models/mvd_tcg_deck.py Normal file

File diff suppressed because it is too large Load Diff

329
models/mvd_tcg_deck_line.py Normal file
View File

@@ -0,0 +1,329 @@
"""MTG-specific deck line extensions."""
from odoo import api, fields, models
from odoo.exceptions import UserError
MTG_DECK_LEGALITY_SELECTION = [
("unknown", "Unknown"),
("legal", "Legal"),
("restricted", "Restricted"),
("not_legal", "Not Legal"),
("banned", "Banned"),
]
class MvdTcgDeckLine(models.Model):
"""Expose MTG card metadata directly on deck lines."""
_inherit = "mvd.tcg.deck.line"
mtg_set_id = fields.Many2one(
related="card_id.mtg_set_id",
string="Set",
readonly=True,
store=True,
index=True,
)
mtg_collector_number = fields.Char(
related="card_id.mtg_collector_number",
string="Collector Number",
readonly=True,
store=True,
index=True,
)
mtg_mana_cost = fields.Char(
related="card_id.mtg_mana_cost",
string="Mana Cost",
readonly=True,
store=True,
)
mtg_mana_value = fields.Float(
related="card_id.mtg_mana_value",
string="Mana Value",
readonly=True,
store=True,
index=True,
)
mtg_type_line = fields.Char(
related="card_id.mtg_type_line",
string="Type Line",
readonly=True,
)
mtg_primary_card_type_id = fields.Many2one(
"mvd.tcg.mtg.card.type",
string="Primary Card Type",
compute="_compute_mtg_primary_card_type",
store=True,
readonly=True,
index=True,
)
mtg_legality_status = fields.Selection(
selection=MTG_DECK_LEGALITY_SELECTION,
string="Legality Status",
compute="_compute_mtg_rule_flags",
store=True,
readonly=True,
index=True,
)
mtg_legality_ok = fields.Boolean(
string="Legality OK",
compute="_compute_mtg_rule_flags",
store=True,
readonly=True,
)
mtg_color_identity_violation = fields.Boolean(
string="Color Identity Violation",
compute="_compute_mtg_rule_flags",
store=True,
readonly=True,
)
mtg_singleton_violation = fields.Boolean(
string="Singleton Violation",
compute="_compute_mtg_rule_flags",
store=True,
readonly=True,
)
mtg_issue_count = fields.Integer(
string="Issue Count",
compute="_compute_mtg_rule_flags",
store=True,
readonly=True,
)
@api.depends(
"card_id.mtg_card_type_ids",
"card_id.mtg_card_type_ids.sequence",
"card_id.mtg_card_type_ids.name",
)
def _compute_mtg_primary_card_type(self):
"""Expose one stable primary card type for grouping and sorting.
Returns:
None: The compute updates records in place.
"""
for line in self:
card_type = line.card_id.mtg_card_type_ids.sorted(
key=lambda current_type: (
current_type.sequence,
current_type.name or "",
current_type.id,
)
)[:1]
line.mtg_primary_card_type_id = card_type
@api.depends(
"board_id.code",
"board_id.include_in_total",
"deck_id.is_mtg_deck",
"deck_id.mtg_format_id",
"deck_id.mtg_format_id.code",
"deck_id.mtg_color_identity_signature",
"deck_id.line_ids.quantity",
"deck_id.line_ids.board_id",
"deck_id.line_ids.board_id.code",
"deck_id.line_ids.board_id.include_in_total",
"deck_id.line_ids.card_id",
"deck_id.line_ids.card_id.mtg_oracle_id",
"deck_id.line_ids.card_id.mtg_type_line",
"deck_id.line_ids.card_id.mtg_oracle_text",
"card_id.mtg_oracle_id",
"card_id.mtg_color_identity_signature",
"card_id.mtg_legality_ids.status",
"card_id.mtg_legality_ids.format_code",
)
def _compute_mtg_rule_flags(self):
"""Compute line-level MTG rule signals for the current deck context.
Returns:
None: The compute updates records in place.
"""
singleton_violation_by_line = {}
legality_status_by_line = {}
legality_ok_by_line = {}
color_violation_by_line = {}
mtg_decks = self.mapped("deck_id").filtered("is_mtg_deck")
for deck in mtg_decks:
format_code = deck.mtg_format_id.code or ""
commander_identity = set(deck.mtg_color_identity_signature or "")
singleton_groups = []
candidate_lines = deck.line_ids.filtered(
lambda line: line.card_id
and line.card_id.game_id.code == "mtg"
and line.board_id.include_in_total
)
candidate_entries = []
for line in candidate_lines:
card = line.card_id.with_context(lang="en_US")
if self._mtg_is_singleton_exempt(card):
continue
singleton_aliases = set(card.mtg_get_singleton_key_aliases())
if not singleton_aliases:
singleton_aliases = {card.external_ref or str(card.id)}
candidate_entries.append(
{
"line": line,
"aliases": singleton_aliases,
}
)
remaining_entries = list(candidate_entries)
while remaining_entries:
seed_entry = remaining_entries.pop(0)
grouped_lines = [seed_entry["line"]]
grouped_aliases = set(seed_entry["aliases"])
did_merge = True
while did_merge:
did_merge = False
unmatched_entries = []
for entry in remaining_entries:
if grouped_aliases & entry["aliases"]:
grouped_lines.append(entry["line"])
grouped_aliases |= entry["aliases"]
did_merge = True
else:
unmatched_entries.append(entry)
remaining_entries = unmatched_entries
singleton_groups.append(grouped_lines)
for grouped_lines in singleton_groups:
total_quantity = sum(line.quantity for line in grouped_lines)
has_violation = total_quantity > 1
for line in grouped_lines:
singleton_violation_by_line[line.id] = has_violation
for line in deck.line_ids:
if not line.card_id or line.card_id.game_id.code != "mtg":
legality_status_by_line[line.id] = "unknown"
legality_ok_by_line[line.id] = True
color_violation_by_line[line.id] = False
continue
legality_status = self._mtg_get_card_legality_status(
line.card_id,
format_code,
)
legality_status_by_line[line.id] = legality_status
legality_ok_by_line[line.id] = legality_status not in {
"banned",
"not_legal",
}
if legality_status == "restricted" and line.quantity > 1:
legality_ok_by_line[line.id] = False
line_identity = set(line.card_id.mtg_color_identity_signature or "")
color_violation_by_line[line.id] = bool(
commander_identity
and line.board_id.code != "command_zone"
and line_identity - commander_identity
)
for line in self:
legality_status = legality_status_by_line.get(line.id, "unknown")
legality_ok = legality_ok_by_line.get(line.id, True)
color_violation = color_violation_by_line.get(line.id, False)
singleton_violation = singleton_violation_by_line.get(line.id, False)
line.mtg_legality_status = legality_status
line.mtg_legality_ok = legality_ok
line.mtg_color_identity_violation = color_violation
line.mtg_singleton_violation = singleton_violation
line.mtg_issue_count = sum(
bool(flag)
for flag in (
not legality_ok,
color_violation,
singleton_violation,
)
)
@api.model
def _mtg_get_card_legality_status(self, card, format_code):
"""Return the legality status of one card for one MTG format.
Args:
card: MTG card record.
format_code: Canonical MTG format code such as ``commander``.
Returns:
str: One status from ``MTG_DECK_LEGALITY_SELECTION``.
"""
if not card or not format_code:
return "unknown"
legality = card.mtg_legality_ids.filtered(
lambda current_legality: current_legality.format_code == format_code
)[:1]
return legality.status if legality else "unknown"
@api.model
def _mtg_is_singleton_exempt(self, card):
"""Return whether a card should bypass singleton checks.
Args:
card: MTG card record in a stable language context.
Returns:
bool: ``True`` when the card can legally appear multiple times.
"""
return bool(card and card.mtg_allows_unlimited_copies())
def _mvd_tcg_validate_move_to_board(self, target_board):
"""Block duplicate MTG singleton cards during board moves."""
self.ensure_one()
result = super()._mvd_tcg_validate_move_to_board(target_board)
deck = self.deck_id
if (
not deck.is_mtg_deck
or not target_board.include_in_total
or not deck._mtg_format_enforces_singleton()
):
return result
english_card = self.card_id.with_context(lang="en_US")
conflict_lines = deck._mtg_get_singleton_conflict_lines(
english_card,
excluding_line=self,
)
total_quantity = self.quantity + sum(conflict_lines.mapped("quantity"))
if total_quantity <= 1:
return result
conflicting_names = ", ".join(conflict_lines.mapped("card_id.display_name")[:3])
raise UserError(
_(
"Commander- and Brawl-style decks can include only one copy of the same card across all styles and printings. "
"Moving this line would conflict with: %(cards)s"
)
% {"cards": conflicting_names or english_card.display_name}
)
def action_move_to_mtg_command_zone(self):
"""Move the current line to the MTG command zone."""
self.ensure_one()
return self._mvd_tcg_move_to_board("command_zone")
def action_move_to_mtg_mainboard(self):
"""Move the current line to the MTG mainboard."""
self.ensure_one()
return self._mvd_tcg_move_to_board("mainboard")
def action_move_to_mtg_sideboard(self):
"""Move the current line to the MTG sideboard."""
self.ensure_one()
return self._mvd_tcg_move_to_board("sideboard")
def action_move_to_mtg_maybeboard(self):
"""Move the current line to the MTG maybeboard."""
self.ensure_one()
return self._mvd_tcg_move_to_board("maybeboard")
def _mvd_tcg_get_card_form_view_xmlid(self):
"""Prefer the MTG-specific card form for MTG deck lines.
Returns:
str: Stable XMLID for the preferred card form view.
"""
self.ensure_one()
if self.game_id.code == "mtg":
return "mvd_tcg_mtg.mvd_tcg_mtg_card_view_form"
return super()._mvd_tcg_get_card_form_view_xmlid()

30
models/mvd_tcg_game.py Normal file
View File

@@ -0,0 +1,30 @@
"""MTG-specific deck defaults."""
from odoo import _, models
class MvdTcgGame(models.Model):
"""Add MTG-specific default deck boards."""
_inherit = "mvd.tcg.game"
def _mvd_tcg_get_default_deck_board_templates(self):
"""Extend default deck boards for MTG decks.
Returns:
list[dict[str, object]]: Ordered board configuration dictionaries.
"""
boards = super()._mvd_tcg_get_default_deck_board_templates()
self.ensure_one()
if self.code != "mtg":
return boards
return [
{
"name": _("Command Zone"),
"code": "command_zone",
"sequence": 5,
"include_in_total": True,
},
*boards,
]

1
report/__init__.py Normal file
View File

@@ -0,0 +1 @@
# MTG deck report customizations are template-only for now.

View File

@@ -0,0 +1,220 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template
id="report_mvd_tcg_deck_document_mtg_inherit"
inherit_id="mvd_tcg_deck.report_mvd_tcg_deck_document"
>
<xpath expr="//td[hasclass('o_mvd_tcg_report_card_cell')]/div[hasclass('o_mvd_tcg_report_card_name')]/span[@t-field='line.card_id']" position="replace">
<span class="o_mvd_tcg_report_card_name_text" t-field="line.card_id"/>
<t t-if="doc.is_mtg_deck and line.card_id.mtg_face_count &lt;= 1">
<t t-set="mana_symbols" t-value="get_mana_symbols(line.mtg_mana_cost)"/>
<t t-if="mana_symbols">
<span class="o_mvd_tcg_report_inline_symbol_group">
<t t-foreach="mana_symbols" t-as="symbol">
<span class="o_mvd_tcg_report_symbol_frame o_mvd_tcg_report_inline_symbol_frame">
<img
class="o_mvd_tcg_report_symbol_icon o_mvd_tcg_report_inline_symbol_icon"
t-att-src="symbol['src']"
t-att-alt="symbol['label']"
t-att-title="symbol['label']"
/>
</span>
</t>
</span>
</t>
<t t-set="line_color_badges" t-value="get_line_color_badges(line)"/>
<t t-if="line_color_badges">
<span class="o_mvd_tcg_report_inline_symbol_group">
<t t-foreach="line_color_badges" t-as="badge">
<span class="o_mvd_tcg_report_symbol_frame o_mvd_tcg_report_inline_symbol_frame">
<img
class="o_mvd_tcg_report_symbol_icon o_mvd_tcg_report_inline_symbol_icon"
t-att-src="badge['src']"
t-att-alt="badge['label']"
t-att-title="badge['label']"
/>
</span>
</t>
</span>
</t>
</t>
</xpath>
<xpath expr="//div[hasclass('o_mvd_tcg_report_extension_hook')]" position="replace">
<div t-if="doc.is_mtg_deck" class="o_mvd_tcg_report_section o_mvd_tcg_report_section_emphasis">
<t t-set="identity_badges" t-value="get_color_badges(doc.mtg_color_identity_signature)"/>
<t t-set="type_breakdown" t-value="get_mtg_type_breakdown(doc)"/>
<h3 class="o_mvd_tcg_report_section_title">Commander Snapshot</h3>
<table class="o_mvd_tcg_report_snapshot" style="margin-bottom: 8px;">
<tbody>
<tr>
<td>
<div class="o_mvd_tcg_report_snapshot_block">
<h4>Format &amp; Commander</h4>
<p style="margin-bottom: 6px;">
<strong>Format:</strong>
<span t-if="doc.mtg_format_id" t-field="doc.mtg_format_id"/>
<span t-else="" class="o_mvd_tcg_report_muted">No format selected</span>
</p>
<p>
<strong>Commander:</strong>
<span t-if="doc.mtg_commander_card_id" t-field="doc.mtg_commander_card_id"/>
<span t-else="" class="o_mvd_tcg_report_muted">No commander detected</span>
</p>
<p style="margin-top: 8px;">
<strong>Deck Size:</strong>
<span t-field="doc.mtg_mainboard_count"/> mainboard
<span class="o_mvd_tcg_report_muted"> · </span>
<span t-field="doc.mtg_command_zone_count"/> command zone
<span t-if="doc.mtg_sideboard_count">
<span class="o_mvd_tcg_report_muted"> · </span>
<span t-field="doc.mtg_sideboard_count"/> sideboard
</span>
</p>
</div>
</td>
<td>
<div class="o_mvd_tcg_report_snapshot_block">
<h4>Color Identity &amp; Curve</h4>
<p style="margin-bottom: 8px;">
<strong>Identity:</strong>
<t t-if="identity_badges">
<span class="o_mvd_tcg_report_symbol_group" style="margin-left: 4px;">
<t t-foreach="identity_badges" t-as="badge">
<span class="o_mvd_tcg_report_symbol_frame o_mvd_tcg_report_identity_frame">
<img
class="o_mvd_tcg_report_symbol_icon o_mvd_tcg_report_identity_icon"
t-att-src="badge['src']"
t-att-alt="badge['label']"
t-att-title="badge['label']"
/>
</span>
</t>
</span>
<span
class="o_mvd_tcg_report_muted"
style="margin-left: 6px; font-weight: 600; color: #334155;"
t-out="doc.mtg_color_identity_name"
/>
</t>
<span t-else="" class="o_mvd_tcg_report_muted">Color identity not available</span>
</p>
<p>
<strong>Average Mana Value:</strong>
<span t-field="doc.mtg_average_mana_value"/>
</p>
<p style="margin-top: 8px;">
<strong>Rule Warnings:</strong>
<span t-field="doc.mtg_rule_warning_count"/>
</p>
</div>
</td>
</tr>
</tbody>
</table>
<table class="o_mvd_tcg_report_snapshot" style="margin-bottom: 4px;">
<tbody>
<tr>
<td>
<div class="o_mvd_tcg_report_snapshot_block">
<h4>Type Composition</h4>
<table class="o_mvd_tcg_report_data_table">
<tbody>
<t t-foreach="type_breakdown" t-as="row">
<tr>
<td style="width: 34%;"><strong t-out="row['label']"/></td>
<td style="width: 16%;"><t t-out="row['value']"/></td>
<td style="width: 50%;">
<div style="height: 8px; background: #e5edf5; border-radius: 999px; overflow: hidden; margin-top: 4px;">
<div
t-att-style="'height:8px;border-radius:999px;background:' + row['accent'] + ';width:' + str(row['bar_width']) + '%;'"
/>
</div>
</td>
</tr>
</t>
</tbody>
</table>
</div>
</td>
<td>
<div class="o_mvd_tcg_report_snapshot_block">
<h4>Rule Notes</h4>
<div t-if="doc.mtg_rule_summary" class="o_mvd_tcg_report_callout">
<div t-field="doc.mtg_rule_summary"/>
</div>
<p t-else="" class="o_mvd_tcg_report_muted">
No MTG rule notes are currently available.
</p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</xpath>
<xpath expr="//td[hasclass('o_mvd_tcg_report_card_cell')]/div[hasclass('o_mvd_tcg_report_card_name')]" position="after">
<t t-set="rule_sections" t-value="get_line_rule_sections(line)"/>
<div
t-if="doc.is_mtg_deck and (line.mtg_type_line or line.mtg_set_id or line.mtg_collector_number or rule_sections)"
class="o_mvd_tcg_report_card_meta"
>
<t t-if="line.card_id.mtg_face_count &gt; 1 and rule_sections">
<div>
<span t-if="line.mtg_set_id" t-field="line.mtg_set_id"/>
<span t-if="line.mtg_set_id and line.mtg_collector_number"> · </span>
<span t-if="line.mtg_collector_number" t-out="line.mtg_collector_number"/>
</div>
<t t-foreach="rule_sections" t-as="section">
<div class="o_mvd_tcg_report_face_block">
<div class="o_mvd_tcg_report_face_title">
<span t-if="section['name']" t-out="section['name']"/>
<t t-set="face_mana_symbols" t-value="get_mana_symbols(section['mana_cost'])"/>
<t t-if="face_mana_symbols">
<span class="o_mvd_tcg_report_inline_symbol_group">
<t t-foreach="face_mana_symbols" t-as="symbol">
<span class="o_mvd_tcg_report_symbol_frame o_mvd_tcg_report_inline_symbol_frame">
<img
class="o_mvd_tcg_report_symbol_icon o_mvd_tcg_report_inline_symbol_icon"
t-att-src="symbol['src']"
t-att-alt="symbol['label']"
t-att-title="symbol['label']"
/>
</span>
</t>
</span>
</t>
</div>
<div t-if="section['type_line']">
<span t-out="section['type_line']"/>
</div>
<div
t-if="section['oracle_text']"
class="o_mvd_tcg_report_oracle"
t-out="render_mtg_rules_text(section['oracle_text'])"
/>
</div>
</t>
</t>
<t t-else="">
<div t-if="line.mtg_type_line">
<span t-field="line.mtg_type_line"/>
</div>
<div>
<span t-if="line.mtg_set_id" t-field="line.mtg_set_id"/>
<span t-if="line.mtg_set_id and line.mtg_collector_number"> · </span>
<span t-if="line.mtg_collector_number" t-out="line.mtg_collector_number"/>
</div>
<div
t-if="rule_sections and rule_sections[0]['oracle_text']"
class="o_mvd_tcg_report_oracle"
t-out="render_mtg_rules_text(rule_sections[0]['oracle_text'])"
/>
</t>
</div>
</xpath>
</template>
</odoo>

21
views/menu_views.xml Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="mvd_tcg_mtg_deck_action" model="ir.actions.act_window">
<field name="name">Decks</field>
<field name="res_model">mvd.tcg.deck</field>
<field name="view_mode">kanban,list,form</field>
<field name="view_id" ref="mvd_tcg_deck.mvd_tcg_deck_view_kanban"/>
<field name="search_view_id" ref="mvd_tcg_deck.mvd_tcg_deck_view_search"/>
<field name="domain" eval="[('game_id', '=', ref('mvd_tcg_mtg.mvd_tcg_game_mtg'))]"/>
<field name="context" eval="{'default_game_id': ref('mvd_tcg_mtg.mvd_tcg_game_mtg')}"/>
</record>
<menuitem
id="mvd_tcg_mtg_decks_menu"
name="Decks"
parent="mvd_tcg_mtg.mvd_tcg_mtg_root_menu"
sequence="20"
action="mvd_tcg_mtg_deck.mvd_tcg_mtg_deck_action"
groups="mvd_tcg_base.mvd_tcg_base_group_user,base.group_system"
/>
</odoo>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="mvd_tcg_mtg_card_view_form_deck_inherit" model="ir.ui.view">
<field name="name">mvd.tcg.mtg.card.view.form.deck.inherit</field>
<field name="model">mvd.tcg.card</field>
<field name="inherit_id" ref="mvd_tcg_mtg.mvd_tcg_mtg_card_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button
name="action_open_decks"
type="object"
class="oe_stat_button"
icon="fa-layer-group"
>
<field name="deck_count" string="Decks" widget="statinfo"/>
</button>
</xpath>
<xpath expr="//header" position="inside">
<button
name="action_open_add_to_deck_wizard"
string="Add to Deck"
type="object"
class="btn-secondary"
/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,433 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="mvd_tcg_deck_line_view_search_mtg_inherit" model="ir.ui.view">
<field name="name">mvd.tcg.deck.line.view.search.mtg.inherit</field>
<field name="model">mvd.tcg.deck.line</field>
<field name="inherit_id" ref="mvd_tcg_deck.mvd_tcg_deck_line_view_search"/>
<field name="arch" type="xml">
<xpath expr="//search" position="inside">
<field name="mtg_set_id"/>
<field name="mtg_primary_card_type_id"/>
<field name="mtg_legality_status"/>
<separator/>
<filter name="filter_mtg_issues" string="Issues" domain="[('mtg_issue_count', '>', 0)]"/>
<filter name="filter_mtg_off_color" string="Off Color" domain="[('mtg_color_identity_violation', '=', True)]"/>
<filter name="filter_mtg_singleton" string="Singleton" domain="[('mtg_singleton_violation', '=', True)]"/>
<filter name="filter_mtg_illegal" string="Illegal" domain="[('mtg_legality_ok', '=', False)]"/>
<group>
<filter name="group_by_type" string="Type" context="{'group_by': 'mtg_primary_card_type_id'}"/>
<filter name="group_by_mv" string="Mana Value" context="{'group_by': 'mtg_mana_value'}"/>
<filter name="group_by_set" string="Set" context="{'group_by': 'mtg_set_id'}"/>
</group>
</xpath>
</field>
</record>
<record id="mvd_tcg_deck_line_view_list_mtg_inherit" model="ir.ui.view">
<field name="name">mvd.tcg.deck.line.view.list.mtg.inherit</field>
<field name="model">mvd.tcg.deck.line</field>
<field name="inherit_id" ref="mvd_tcg_deck.mvd_tcg_deck_line_view_list"/>
<field name="arch" type="xml">
<xpath expr="//list" position="attributes">
<attribute name="default_order">primary_role_sequence, mtg_mana_value, sequence, id</attribute>
<attribute name="decoration-danger">mtg_color_identity_violation or mtg_singleton_violation or mtg_legality_status in ('banned', 'not_legal')</attribute>
<attribute name="decoration-warning">mtg_legality_status == 'restricted'</attribute>
</xpath>
<xpath expr="//field[@name='card_id']" position="after">
<field name="mtg_mana_cost" widget="mtg_symbols" optional="show"/>
<field name="mtg_mana_value" optional="hide"/>
<field name="mtg_type_line" optional="show"/>
<field name="mtg_legality_status" optional="hide"/>
<field name="mtg_color_identity_violation" optional="hide"/>
<field name="mtg_singleton_violation" optional="hide"/>
</xpath>
</field>
</record>
<record id="mvd_tcg_deck_board_view_form_mtg_inherit" model="ir.ui.view">
<field name="name">mvd.tcg.deck.board.view.form.mtg.inherit</field>
<field name="model">mvd.tcg.deck.board</field>
<field name="inherit_id" ref="mvd_tcg_deck.mvd_tcg_deck_board_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='line_ids']" position="attributes">
<attribute name="readonly">0</attribute>
</xpath>
<xpath expr="//field[@name='line_ids']/list" position="attributes">
<attribute name="editable">bottom</attribute>
<attribute name="default_order">primary_role_sequence, mtg_mana_value, sequence, id</attribute>
<attribute name="decoration-danger">mtg_color_identity_violation or mtg_singleton_violation or mtg_legality_status in ('banned', 'not_legal')</attribute>
<attribute name="decoration-warning">mtg_legality_status == 'restricted'</attribute>
</xpath>
<xpath expr="//field[@name='line_ids']/list/field[@name='card_id']" position="after">
<field name="mtg_mana_cost" widget="mtg_symbols" optional="show"/>
<field name="mtg_mana_value" optional="hide"/>
<field name="mtg_type_line" optional="show"/>
<field name="primary_role_id" optional="show"/>
<field name="mtg_legality_status" optional="hide"/>
</xpath>
</field>
</record>
<record id="mvd_tcg_deck_view_form_mtg_inherit" model="ir.ui.view">
<field name="name">mvd.tcg.deck.view.form.mtg.inherit</field>
<field name="model">mvd.tcg.deck</field>
<field name="inherit_id" ref="mvd_tcg_deck.mvd_tcg_deck_view_form"/>
<field name="arch" type="xml">
<xpath expr="//sheet" position="inside">
<field name="is_mtg_deck" invisible="1"/>
<field name="mtg_command_zone_board_id" invisible="1"/>
<field name="mtg_mainboard_board_id" invisible="1"/>
<field name="mtg_sideboard_board_id" invisible="1"/>
<field name="mtg_maybeboard_board_id" invisible="1"/>
</xpath>
<xpath expr="//page[@name='overview']" position="inside">
<group string="Deck Snapshot" invisible="not is_mtg_deck">
<group>
<field name="mtg_format_id"/>
<field name="mtg_command_zone_count" readonly="1"/>
<field name="mtg_mainboard_count" readonly="1"/>
<field name="mtg_sideboard_count" readonly="1"/>
<field name="mtg_maybeboard_count" readonly="1"/>
</group>
<group>
<field
name="mtg_color_identity_name"
readonly="1"
invisible="not mtg_color_identity_name"
/>
<field name="mtg_average_mana_value" readonly="1"/>
</group>
</group>
</xpath>
<xpath expr="//page[@name='boards']" position="attributes">
<attribute name="invisible">is_mtg_deck</attribute>
</xpath>
<xpath expr="//page[@name='cards']" position="attributes">
<attribute name="invisible">is_mtg_deck</attribute>
</xpath>
<xpath expr="//page[@name='overview']" position="after">
<page string="Command Zone" name="mtg_command_zone" invisible="not is_mtg_deck">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="text-muted">
<strong class="me-1">Cards</strong>
<field name="mtg_command_zone_count"/>
</div>
<button
name="action_add_to_mtg_command_zone"
type="object"
string="Add Card"
class="btn-primary"
/>
<button
name="action_open_mtg_command_zone"
type="object"
string="Manage Lines"
class="btn-secondary"
invisible="not mtg_command_zone_board_id"
/>
</div>
<field name="mtg_command_zone_line_ids" nolabel="1" readonly="0" mode="list,form">
<list create="0" delete="1" editable="bottom" default_order="primary_role_sequence, mtg_mana_value, sequence, id" decoration-danger="mtg_color_identity_violation or mtg_singleton_violation or mtg_legality_status in ('banned', 'not_legal')" decoration-warning="mtg_legality_status == 'restricted'">
<field name="sequence" widget="handle" optional="hide"/>
<field
name="card_image_128"
widget="mvd_deck_zoom_image"
optional="show"
options="{'preview_image': 'card_image_1920', 'zoom': true, 'zoom_delay': 0, 'size': [0, 96]}"
/>
<field name="quantity"/>
<field name="card_id"/>
<field name="mtg_mana_cost" widget="mtg_symbols" optional="show"/>
<field name="mtg_type_line" optional="show"/>
<field name="primary_role_id" optional="show"/>
<field name="mtg_legality_status" optional="hide"/>
<field name="role_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="show"/>
<field name="note" optional="show"/>
</list>
</field>
</page>
<page string="Mainboard" name="mtg_mainboard" invisible="not is_mtg_deck">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="text-muted">
<strong class="me-1">Cards</strong>
<field name="mtg_mainboard_count"/>
</div>
<button
name="action_add_to_mtg_mainboard"
type="object"
string="Add Card"
class="btn-primary"
/>
<button
name="action_open_mtg_mainboard"
type="object"
string="Manage Lines"
class="btn-secondary"
invisible="not mtg_mainboard_board_id"
/>
</div>
<field name="mtg_mainboard_line_ids" nolabel="1" readonly="0" mode="list,form">
<list create="0" delete="1" editable="bottom" default_order="primary_role_sequence, mtg_mana_value, sequence, id" decoration-danger="mtg_color_identity_violation or mtg_singleton_violation or mtg_legality_status in ('banned', 'not_legal')" decoration-warning="mtg_legality_status == 'restricted'">
<field name="sequence" widget="handle" optional="hide"/>
<field
name="card_image_128"
widget="mvd_deck_zoom_image"
optional="show"
options="{'preview_image': 'card_image_1920', 'zoom': true, 'zoom_delay': 0, 'size': [0, 96]}"
/>
<field name="quantity"/>
<field name="card_id"/>
<field name="mtg_mana_cost" widget="mtg_symbols" optional="show"/>
<field name="mtg_mana_value" optional="hide"/>
<field name="mtg_type_line" optional="show"/>
<field name="primary_role_id" optional="show"/>
<field name="mtg_legality_status" optional="hide"/>
<field name="role_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="show"/>
<field name="note" optional="show"/>
</list>
</field>
</page>
<page string="Sideboard" name="mtg_sideboard" invisible="not is_mtg_deck">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="text-muted">
<strong class="me-1">Cards</strong>
<field name="mtg_sideboard_count"/>
</div>
<button
name="action_add_to_mtg_sideboard"
type="object"
string="Add Card"
class="btn-primary"
/>
<button
name="action_open_mtg_sideboard"
type="object"
string="Manage Lines"
class="btn-secondary"
invisible="not mtg_sideboard_board_id"
/>
</div>
<field name="mtg_sideboard_line_ids" nolabel="1" readonly="0" mode="list,form">
<list create="0" delete="1" editable="bottom" default_order="primary_role_sequence, mtg_mana_value, sequence, id" decoration-danger="mtg_color_identity_violation or mtg_singleton_violation or mtg_legality_status in ('banned', 'not_legal')" decoration-warning="mtg_legality_status == 'restricted'">
<field name="sequence" widget="handle" optional="hide"/>
<field
name="card_image_128"
widget="mvd_deck_zoom_image"
optional="show"
options="{'preview_image': 'card_image_1920', 'zoom': true, 'zoom_delay': 0, 'size': [0, 96]}"
/>
<field name="quantity"/>
<field name="card_id"/>
<field name="mtg_mana_cost" widget="mtg_symbols" optional="show"/>
<field name="mtg_type_line" optional="show"/>
<field name="primary_role_id" optional="show"/>
<field name="mtg_legality_status" optional="hide"/>
<field name="role_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="show"/>
<field name="note" optional="show"/>
</list>
</field>
</page>
<page string="Maybeboard" name="mtg_maybeboard" invisible="not is_mtg_deck">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="text-muted">
<strong class="me-1">Cards</strong>
<field name="mtg_maybeboard_count"/>
</div>
<button
name="action_add_to_mtg_maybeboard"
type="object"
string="Add Card"
class="btn-primary"
/>
<button
name="action_open_mtg_maybeboard"
type="object"
string="Manage Lines"
class="btn-secondary"
invisible="not mtg_maybeboard_board_id"
/>
</div>
<field name="mtg_maybeboard_line_ids" nolabel="1" readonly="0" mode="list,form">
<list create="0" delete="1" editable="bottom" default_order="primary_role_sequence, mtg_mana_value, sequence, id" decoration-danger="mtg_color_identity_violation or mtg_singleton_violation or mtg_legality_status in ('banned', 'not_legal')" decoration-warning="mtg_legality_status == 'restricted'">
<field name="sequence" widget="handle" optional="hide"/>
<field
name="card_image_128"
widget="mvd_deck_zoom_image"
optional="show"
options="{'preview_image': 'card_image_1920', 'zoom': true, 'zoom_delay': 0, 'size': [0, 96]}"
/>
<field name="quantity"/>
<field name="card_id"/>
<field name="mtg_mana_cost" widget="mtg_symbols" optional="show"/>
<field name="mtg_type_line" optional="show"/>
<field name="primary_role_id" optional="show"/>
<field name="mtg_legality_status" optional="hide"/>
<field name="role_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="show"/>
<field name="note" optional="show"/>
</list>
</field>
</page>
<page string="Analysis" name="mtg_analysis" invisible="not is_mtg_deck">
<div class="row g-3">
<div class="col-12 col-md-6 col-xl-3">
<div class="border rounded-3 p-3 h-100 bg-body">
<div class="text-uppercase text-muted small fw-semibold">Average Mana Value</div>
<div class="fs-4 fw-bold mt-1">
<field name="mtg_average_mana_value" readonly="1"/>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-3">
<div class="border rounded-3 p-3 h-100 bg-body">
<div class="text-uppercase text-muted small fw-semibold">Tagged Cards</div>
<div class="fs-4 fw-bold mt-1">
<field name="mtg_tagged_line_count" readonly="1"/>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-3">
<div class="border rounded-3 p-3 h-100 bg-body">
<div class="text-uppercase text-muted small fw-semibold">Untagged Cards</div>
<div class="fs-4 fw-bold mt-1">
<field name="mtg_untagged_line_count" readonly="1"/>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-3">
<div class="border rounded-3 p-3 h-100 bg-body">
<div class="text-uppercase text-muted small fw-semibold">Role Coverage %</div>
<div class="fs-4 fw-bold mt-1">
<field name="mtg_role_coverage_ratio" readonly="1"/>
</div>
</div>
</div>
</div>
<group string="Deck Metrics" class="mt-3">
<group>
<field name="mtg_land_count" readonly="1"/>
<field name="mtg_creature_count" readonly="1"/>
<field name="mtg_artifact_count" readonly="1"/>
<field name="mtg_enchantment_count" readonly="1"/>
</group>
<group>
<field name="mtg_planeswalker_count" readonly="1"/>
<field name="mtg_instant_count" readonly="1"/>
<field name="mtg_sorcery_count" readonly="1"/>
<field name="mtg_issue_line_count" readonly="1"/>
</group>
</group>
<div class="row g-3 mt-1">
<div class="col-12 col-xl-6">
<div class="border rounded-3 p-3 h-100 bg-body">
<div class="fw-semibold mb-2">Mana Curve</div>
<field name="mtg_mana_curve_html" widget="html" nolabel="1" readonly="1"/>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="border rounded-3 p-3 h-100 bg-body">
<div class="fw-semibold mb-2">Role Breakdown</div>
<field name="mtg_role_breakdown_html" widget="html" nolabel="1" readonly="1"/>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="border rounded-3 p-3 h-100 bg-body">
<div class="fw-semibold mb-2">Type Composition</div>
<field name="mtg_type_breakdown_html" widget="html" nolabel="1" readonly="1"/>
</div>
</div>
<div class="col-12 col-xl-6">
<div class="border rounded-3 p-3 h-100 bg-body">
<div class="fw-semibold mb-2">Color Pips</div>
<field name="mtg_color_pip_breakdown_html" widget="html" nolabel="1" readonly="1"/>
</div>
</div>
</div>
</page>
</xpath>
</field>
</record>
<record id="mvd_tcg_deck_line_view_form_mtg_inherit" model="ir.ui.view">
<field name="name">mvd.tcg.deck.line.view.form.mtg.inherit</field>
<field name="model">mvd.tcg.deck.line</field>
<field name="inherit_id" ref="mvd_tcg_deck.mvd_tcg_deck_line_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button
name="action_move_to_mtg_command_zone"
type="object"
class="oe_stat_button"
icon="fa-star"
invisible="game_id.code != 'mtg' or board_id.code == 'command_zone'"
>
<div class="o_stat_info">
<span class="o_stat_text">Command</span>
</div>
</button>
<button
name="action_move_to_mtg_mainboard"
type="object"
class="oe_stat_button"
icon="fa-clone"
invisible="game_id.code != 'mtg' or board_id.code == 'mainboard'"
>
<div class="o_stat_info">
<span class="o_stat_text">Mainboard</span>
</div>
</button>
<button
name="action_move_to_mtg_sideboard"
type="object"
class="oe_stat_button"
icon="fa-columns"
invisible="game_id.code != 'mtg' or board_id.code == 'sideboard'"
>
<div class="o_stat_info">
<span class="o_stat_text">Sideboard</span>
</div>
</button>
<button
name="action_move_to_mtg_maybeboard"
type="object"
class="oe_stat_button"
icon="fa-lightbulb-o"
invisible="game_id.code != 'mtg' or board_id.code == 'maybeboard'"
>
<div class="o_stat_info">
<span class="o_stat_text">Maybeboard</span>
</div>
</button>
</xpath>
<xpath expr="//div[hasclass('d-flex') and hasclass('gap-2') and hasclass('flex-wrap')]" position="inside">
<field name="mtg_set_id" widget="badge" invisible="not mtg_set_id"/>
<field name="mtg_collector_number" widget="badge" invisible="not mtg_collector_number"/>
<field name="mtg_legality_status" widget="badge"/>
</xpath>
<xpath expr="//group[1]" position="after">
<group string="Card Snapshot">
<group>
<field name="mtg_mana_cost" widget="mtg_symbols" readonly="1" invisible="not mtg_mana_cost"/>
<field name="mtg_mana_value" readonly="1"/>
<field name="mtg_primary_card_type_id" readonly="1"/>
</group>
<group>
<field name="mtg_type_line" widget="text" options="{'line_breaks': False}" readonly="1" invisible="not mtg_type_line"/>
<field name="mtg_color_identity_violation" widget="boolean_toggle" readonly="1"/>
<field name="mtg_singleton_violation" widget="boolean_toggle" readonly="1"/>
<field name="mtg_legality_ok" widget="boolean_toggle" readonly="1"/>
</group>
</group>
</xpath>
</field>
</record>
</odoo>