Template Melya

This commit is contained in:
2026-05-28 13:34:56 +02:00
parent 5b5685ae12
commit 26f43c2438
6 changed files with 184 additions and 15 deletions

View File

@@ -0,0 +1 @@
,PC_LO/baron,PC_LO,28.05.2026 13:23,file:///C:/Users/baron/AppData/Roaming/LibreOffice/4;

View File

@@ -1584,12 +1584,11 @@
<text:p text:style-name="P8"><text:span text:style-name="Police_20_par_20_défaut"><text:span text:style-name="T6">Payment terms:</text:span></text:span></text:p>
<text:p text:style-name="P8"><text:span text:style-name="Police_20_par_20_défaut"><text:span text:style-name="T9"><text:placeholder text:placeholder-type="text">&lt;invoice.report_payment_description or &apos;&apos;&gt;</text:placeholder></text:span></text:span><text:span text:style-name="Police_20_par_20_défaut"><text:span text:style-name="T9"> </text:span></text:span></text:p>
<text:p text:style-name="P4"/>
<text:p text:style-name="P21">Bank: UBS SWITZERLAND AG</text:p>
<text:p text:style-name="P8"><text:span text:style-name="Police_20_par_20_défaut"><text:span text:style-name="T9">Case Postale </text:span></text:span></text:p>
<text:p text:style-name="P8"><text:span text:style-name="Police_20_par_20_défaut"><text:span text:style-name="T9">CH-1211 Genève 2</text:span></text:span></text:p>
<text:p text:style-name="P21"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;invoice.report_melya_bank_name&quot;&gt;</text:placeholder>Bank: <text:placeholder text:placeholder-type="text">&lt;invoice.report_melya_bank_name&gt;</text:placeholder><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P8"><text:span text:style-name="Police_20_par_20_défaut"><text:span text:style-name="T9"><text:placeholder text:placeholder-type="text">&lt;for each=&quot;line in invoice.report_melya_bank_address.splitlines()&quot;&gt;</text:placeholder><text:placeholder text:placeholder-type="text">&lt;line&gt;</text:placeholder><text:placeholder text:placeholder-type="text">&lt;/for&gt;</text:placeholder></text:span></text:span></text:p>
<text:p text:style-name="P4"/>
<text:p text:style-name="P22">IBAN : CH36 0021 5215 2487 7461 D</text:p>
<text:p text:style-name="P22">SwifT Code: UBSWCHZH80A</text:p>
<text:p text:style-name="P22"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;invoice.report_melya_bank_iban&quot;&gt;</text:placeholder>IBAN : <text:placeholder text:placeholder-type="text">&lt;invoice.report_melya_bank_iban&gt;</text:placeholder><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P22"><text:placeholder text:placeholder-type="text">&lt;if test=&quot;invoice.report_melya_bank_swift&quot;&gt;</text:placeholder>SwifT Code: <text:placeholder text:placeholder-type="text">&lt;invoice.report_melya_bank_swift&gt;</text:placeholder><text:placeholder text:placeholder-type="text">&lt;/if&gt;</text:placeholder></text:p>
<text:p text:style-name="P4"/>
</table:table-cell>
</table:table-row>

View File

@@ -644,11 +644,74 @@ class Invoice(metaclass=PoolMeta):
bank = getattr(account, 'bank', None) if account else None
return getattr(bank, 'bic', None) or ''
@staticmethod
def _get_report_bank_address(account):
bank = getattr(account, 'bank', None) if account else None
party = getattr(bank, 'party', None) if bank else None
address_get = getattr(party, 'address_get', None) if party else None
if callable(address_get):
return address_get()
return None
@staticmethod
def _get_report_bank_party_name(account):
bank = getattr(account, 'bank', None) if account else None
party = getattr(bank, 'party', None) if bank else None
address = Invoice._get_report_bank_address(account)
return (
getattr(address, 'party_full_name', None)
or getattr(address, 'party_name', None)
or getattr(party, 'rec_name', None)
or getattr(bank, 'rec_name', None)
or '')
@staticmethod
def _get_report_bank_address_lines(account):
address = Invoice._get_report_bank_address(account)
full_address = getattr(address, 'full_address', None) if address else ''
if not full_address and address:
city_line = ' '.join(filter(None, [
getattr(address, 'postal_code', None),
getattr(address, 'city', None),
]))
full_address = '\n'.join(filter(None, [
getattr(address, 'name', None),
getattr(address, 'street', None),
city_line,
]))
return '\n'.join(
line for line in (full_address or '').splitlines()
if line.strip())
@staticmethod
def _format_report_payment_amount(value):
amount = Decimal(str(value or 0)).quantize(Decimal('0.01'))
return format(amount, 'f')
def _get_report_melya_bank_account(self):
sale = self._get_report_deal_sale()
return getattr(sale, 'our_bank_account', None) if sale else None
@property
def report_melya_bank_name(self):
return self._get_report_bank_party_name(
self._get_report_melya_bank_account())
@property
def report_melya_bank_address(self):
return self._get_report_bank_address_lines(
self._get_report_melya_bank_account())
@property
def report_melya_bank_iban(self):
return self._get_report_bank_account_number(
self._get_report_melya_bank_account())
@property
def report_melya_bank_swift(self):
return self._get_report_bank_swift(
self._get_report_melya_bank_account())
@property
def _report_payment_order_company_account(self):
return self._get_report_bank_account(getattr(self.company, 'party', None))

View File

@@ -784,16 +784,51 @@ class Sale(metaclass=PoolMeta):
@property
def report_deal(self):
if self.lines and self.lines[0].lots and len(self.lines[0].lots) > 1:
purchase = getattr(getattr(self.lines[0].lots[1], 'line', None),
'purchase', None)
purchase_number = getattr(purchase, 'number', None)
purchase_number = self._format_report_deal_number(
purchase_number, 'P')
sale_number = self._format_report_deal_number(self.number, 'S')
return ' '.join(
number for number in [purchase_number, sale_number] if number)
return ''
purchase = self._get_report_deal_purchase()
purchase_number = self._format_report_deal_number(
getattr(purchase, 'number', None), 'P')
sale_number = self._format_report_deal_number(
getattr(self, 'number', None), 'S')
return ' '.join(
number for number in [purchase_number, sale_number] if number)
def _get_report_deal_purchase(self):
lots = []
for line in getattr(self, 'lines', []) or []:
lots.extend(getattr(line, 'lots', []) or [])
for lot in lots:
purchase = getattr(getattr(lot, 'line', None), 'purchase', None)
if purchase:
return purchase
if not lots:
return None
LotQt = Pool().get('lot.qt')
for lot in lots:
lot_ids = []
lot_id = getattr(lot, 'id', None)
if lot_id:
lot_ids.append(lot_id)
get_vlot_s = getattr(lot, 'getVlot_s', None)
if get_vlot_s:
virtual_lot = get_vlot_s()
virtual_lot_id = getattr(virtual_lot, 'id', None)
if virtual_lot_id:
lot_ids.append(virtual_lot_id)
for lot_id in lot_ids:
lot_qts = LotQt.search([
('lot_s', '=', lot_id),
('lot_p', '>', 0),
], limit=1)
for lot_qt in lot_qts:
purchase = getattr(
getattr(getattr(lot_qt, 'lot_p', None), 'line', None),
'purchase', None)
if purchase:
return purchase
return None
@staticmethod
def _format_report_deal_number(number, prefix):

View File

@@ -3259,6 +3259,75 @@ class PurchaseTradeTestCase(ModuleTestCase):
self.assertEqual(invoice.report_contract_number, 'P-12/S-34')
def test_invoice_melya_bank_uses_sale_our_bank_account(self):
'invoice_melya bank block uses the sale company bank account'
Invoice = Pool().get('account.invoice')
address = Mock(
party_full_name='UBS SWITZERLAND AG',
full_address='Case Postale\nCH-1211 Geneve 2',
)
party = Mock(rec_name='UBS')
party.address_get.return_value = address
bank = Mock(party=party, bic='UBSWCHZH80A')
account = Mock(
bank=bank,
numbers=[Mock(type='iban', number='CH36 0021 5215 2487 7461 D')],
)
sale = Mock(our_bank_account=account)
invoice = Invoice()
invoice._get_report_deal_sale = Mock(return_value=sale)
self.assertEqual(invoice.report_melya_bank_name, 'UBS SWITZERLAND AG')
self.assertEqual(
invoice.report_melya_bank_address,
'Case Postale\nCH-1211 Geneve 2')
self.assertEqual(
invoice.report_melya_bank_iban,
'CH36 0021 5215 2487 7461 D')
self.assertEqual(invoice.report_melya_bank_swift, 'UBSWCHZH80A')
def test_sale_report_deal_uses_purchase_and_sale_numbers(self):
'sale deal number includes the linked purchase number'
Sale = Pool().get('sale.sale')
purchase = Mock(number='12')
lot = Mock(line=Mock(purchase=purchase))
sale = Sale()
sale.number = '88'
sale.lines = [Mock(lots=[lot])]
self.assertEqual(sale.report_deal, 'P-12 S-88')
self.assertEqual(sale.report_melya_proforma_number, 'P-12 S-88')
def test_sale_report_deal_falls_back_to_lot_qt_matching(self):
'sale deal number finds purchase through lot.qt matching'
Sale = Pool().get('sale.sale')
class SaleLot:
id = 99
line = None
purchase = Mock(number='12')
lot_qt_model = Mock()
lot_qt_model.search.return_value = [
Mock(lot_p=Mock(line=Mock(purchase=purchase)))]
sale = Sale()
sale.number = '88'
sale.lines = [Mock(lots=[SaleLot()])]
with patch('trytond.modules.purchase_trade.sale.Pool') as PoolMock:
PoolMock.return_value.get.return_value = lot_qt_model
self.assertEqual(sale.report_deal, 'P-12 S-88')
lot_qt_model.search.assert_called_once_with([
('lot_s', '=', 99),
('lot_p', '>', 0),
], limit=1)
def test_contract_factory_uses_one_line_per_selected_source(self):
'matched multi-lot contract creation keeps one generated line per source lot'
contract_detail = Mock(quantity=Decimal('2000'))

View File

@@ -662,9 +662,11 @@
</style:style>
<style:style style:name="P63" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:margin-left="10.76cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false" style:writing-mode="lr-tb"/>
<style:text-properties style:font-name="Arial mt" fo:font-size="9pt" style:font-size-asian="9pt" style:font-size-complex="9pt"/>
</style:style>
<style:style style:name="P64" style:family="paragraph" style:parent-style-name="P1">
<style:paragraph-properties fo:margin-left="10.76cm" fo:margin-right="0cm" fo:text-indent="0cm" style:auto-text-indent="false" style:writing-mode="lr-tb"/>
<style:text-properties style:font-name="Arial1" fo:font-size="10pt" style:font-size-asian="10pt" style:font-size-complex="10pt"/>
</style:style>
<style:style style:name="MelyaFooterVat" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties fo:text-align="center" style:justify-single-word="false" fo:border-top="0.5pt solid #808080" fo:border-bottom="0.5pt solid #808080" fo:padding-top="0.12cm" fo:padding-bottom="0.12cm"/>