# 📓 Colibri

**Scopul**

**Viziunea**<span> noastră este să creăm o afacere profitabilă care nu necesită implicare non-stop, în care să putem lucra din plăcere la lucrurile la care suntem pricepuți și ne încarcă de energie, să ne ofere sens, și în care putem avea un program flexibil.</span>

**Misiunea**<span> curentă este să o facem profitabilă.</span>

**Rezultate cheie**

[**Indicatori 2025**](https://cloud.flexbiz.ro/apps/files/files/2656?dir=/Companies/Colibri/wiki&editing=false&openfile=true)

[**Indicatori 2026**](https://cloud.flexbiz.ro/apps/files/files/239053?dir=/Companies/Colibri/wiki&editing=false&openfile=true)

**Resurse**

[**Sarcini**](https://cloud.flexbiz.ro/f/241178)

[**Calculator livrare**](https://docs.google.com/spreadsheets/d/1FA53hmTlfpVAXm3kVwrUekswZiVkJcZOgMHTpx4BPZA/edit?gid=924751351#gid=924751351)

**Acțiuni**

- bani investiți = împrumut cu dobândă 15%

# 📜 Povestea companiei

Utilizatorul creează produsele.

Utilizatorul comandă marfă, apoi când ajunge recepționează marfa și factura.

Utilizatorul creează contul clientului, apoi vinde marfa.

La sfârșit de lună se reconciliază extrasul bancar.

# 👨‍💼 Managerul de magazin

# Managerul de magazin comandă marfă

**Managerul de magazin** setează stocul minim și furnizorul principal pentru toate produsele de stoc. Când **Managerul de magazin** observă că un produs lipsește, verifică ca stocul minim și furnizorul să fie setat și apoi adaugă necesar la comandă. **Managerul de magazin** face necesarul și comandă marfa.

## Setează stocul minim și furnizorul principal

<iframe allow="fullscreen" height="" loading="lazy" src="https://videos.flexbiz.ro/colibri/comandamarfasetarestocminim/embed/" style="aspect-ratio: 1.7777778; border: none; width: 100%;" title="Video player widget for "Embed – comanda-marfa-setare-stoc-minim"" width=""></iframe>

## Comandă marfă

### **Video**

<iframe allow="fullscreen" height="" loading="lazy" src="https://videos.flexbiz.ro/colibri/comandamarfa/embed/" style="aspect-ratio: 1.7777778; border: none; width: 100%;" title="Video player widget for "Embed – comanda-marfa"" width=""></iframe>

### **Pașii principali**

#### PASUL 1 – Căutarea furnizorului și adăugarea produselor

- Se caută furnizorul (ex: Candelco).
- Se identifică produsele lipsă (ex: ipsos, ciment etc.).
- Se adaugă produsele la necesar.

#### PASUL 2 – Verificarea secțiunii „Aprobă”

- Se verifică întotdeauna secțiunea **„Aprobă”**.

🔴 Observație importantă: Verificarea acestei secțiuni este obligatorie înainte de trimiterea comenzii.

#### PASUL 3 – Plasarea comenzii

- Se accesează secțiunea „Aprobă”.
- Se verifică produsele adăugate.
- Se selectează produsele.
- Se apasă „Comandă” și se trimite comanda.

### **Cadența**

- Ori de câte ori se plasează o comandă către furnizor.
- Pe parcursul săptămânii, pe măsură ce se adaugă produse în secțiunea „Aprobă”.
- La momentul necesar reaprovizionării.

### **Checklist**

✔️ Se verifică întotdeauna secțiunea „Aprobă”

## Comandă marfă - Dunca

<iframe allow="fullscreen" height="" loading="lazy" src="https://videos.flexbiz.ro/colibri/comandamarfadunca/embed/" style="aspect-ratio: 1.7777778; border: none; width: 100%;" title="Video player widget for "Embed – comanda-marfa-dunca"" width=""></iframe>

## Comandă marfă - Transfer

<iframe allow="fullscreen" height="" loading="lazy" src="https://videos.flexbiz.ro/colibri/comandamarfatransfer/embed/" style="aspect-ratio: 1.7777778; border: none; width: 100%;" title="Video player widget for "Embed – comanda-marfa-transfer"" width=""></iframe>

# Managerul de magazin creează produsele

## **Managerul de magazin** stabilește prețurile și promoțiile produselor.

<iframe allow="fullscreen" height="" loading="lazy" src="https://videos.flexbiz.ro/colibri/seteazapreturi/embed/" style="aspect-ratio: 1.7777778; border: none; width: 100%;" title="Video player widget for "Embed – seteaza-preturi"" width=""></iframe>

## **Modificare cote TVA**

### **Video**

<iframe allow="fullscreen" height="" loading="lazy" src="https://videos.flexbiz.ro/colibri/modificarecotetva/embed/" style="aspect-ratio: 1.7777778; border: none; width: 100%;" title="Video player widget for "Embed – modificare-cote-tva"" width=""></iframe>

### **Pașii principali**

1. Modificarea cotelor de TVA în Casa de marcat de către firma care vă asigură service-ul casei de marcat
2. Modificarea cotei standard
3. Modificarea cotelor de TVA pe produs(opțional)

# Managerul de magazin recepționează factura

## **Printare Receptie - Grupeaza dupa TVA la achizitie**

**<iframe allowfullscreen="allowfullscreen" height="" src="https://www.youtube.com/embed/K9VQU-0d1GE" width="100%"></iframe>**

# 💰 Vânzătorul

# Vânzătorul vinde marfa

## **Vânzătorul** introduce produsele și datele clientului.

<iframe allow="fullscreen" height="" loading="lazy" src="https://videos.flexbiz.ro/colibri/folosestepreturi/embed/" style="aspect-ratio: 1.7777778; border: none; width: 100%;" title="Video player widget for "Embed – foloseste-preturi"" width=""></iframe>

# 💻 Sistemul

# Managerul de magazin comandă marfă

## Data mapping

The suppliers for a product are distinct for each facility. There is a party for each Facility. Suppliers are stored in mantle.product.ProductPrice, using the following fields:

- **productPriceId**: supplierId+"\_"+productId+"\_"+facilityPartyId
- **productId**: productId
- **vendorPartyId**: supplierId
- **customerPartyId**: facilityPartyId
- **priceTypeEnumId**: "PptCurrent"
- **pricePurposeEnumId**: "PppPurchase"
- **price**: price
- **priceUomId**: "RON"
- **preferredOrderEnumId**: "SpoMain" or "SpoAlternate"
- **otherPartyItemId**: supplierProductId
- **otherPartyItemName**: supplierProductName

Product Pareto category is stored as:

```
mantle.product.category.ProductCategoryMember
```

- **productCategoryId**: facilityPartyId+paretoCategory (eg.: L2A)
- **productId**: productId

```
mantle.product.category.ProductCategory
```

- **productCategoryId**: facilityPartyId+paretoCategory (eg.: L2A)
- **productCategoryTypeEnumId**: PctPareto
- **categoryName**: "Colibri Pareto A"
- **ownerPartyId**: L2

Products that should be kept in stock and replenished when sold are stored as:

```
mantle.product.Product
```

- **requireInventory**: Y or null

```
mantle.facility.ProductFacility:
```

- **productId**: productId
- **facilityId**: facilityId
- **minimumStock**: minimumStock

Product ordering requirements are stored as:

```
mantle.request.requirement.Requirement
```

- **requirementTypeEnumId**: RqTpInventory
- **statusId**: statusId
- **facilityId**: facilityId ?: ec.user.getPreference('FacilityActive') ?: ec.user.getPreference('FacilityGeneralDefault')
- **productId**: productId
- **quantity**: quantity
- **description**: supplier.organizationName

## Screen outline

### Necesar

- search widgets
- table 
    - cod
    - denumire
    - UM
    - pareto
    - ultimul preț achiziție fără TVA
    - preț vânzare
    - furnizori
    - stocAchCuTVA (hidden)
    - DIO(zile epuizare stoc) (hidden)
    - stocAchCuTVA \* DIO (hidden)
    - stocMinim
    - stoc L1
    - stoc L2
    - comanda recomandată L2
    - comenzi furnizor L2
    - adauga
- refresh button

### Aproba

- search widgets
- Comanda
- Delete
- table 
    - Cod
    - Denumire
    - UM
    - ULPfTVA
    - preț vânzare
    - Furnizori
    - Necesar
- Comanda popup 
    - furnizor
    - trimite pe

### Comenzi

- search widgets
- Delete
- table 
    - Cod
    - Denumire
    - UM
    - Furnizor
    - Comandat

## Screen data mapping

### Necesar

<table border="1" id="bkmrk-table-products-where"><tbody><tr><td>**Table**</td><td>**Products** WHERE Product.ACTIV\_FIELD = true AND Product.HIDE\_WHEN\_ORDERING\_FIELD = false  
**mantle.product.ProductPrices** WHERE requireInventory = Y/null AND customerPartyId = this gestiune partyId(L2)  
AND priceTypeEnumId = PptCurrent AND pricePurposeEnumId = PppPurchase</td></tr><tr><td>cod</td><td>Product.BARCODE\_FIELD</td></tr><tr><td>denumire</td><td>Product.NAME\_FIELD</td></tr><tr><td>UM</td><td>Product.UOM\_FIELD</td></tr><tr><td>Pareto

</td><td>mantle.product.category.ProductCategoryMember.productCategoryId</td></tr><tr><td>ultimul preț achiziție fără TVA</td><td>Product.LAST\_BUYING\_PRICE\_FIELD</td></tr><tr><td>preț vânzare</td><td>Product.PRICE\_FIELD</td></tr><tr><td>furnizori

</td><td>**Organization.organizationName** from ProductPrice.vendorPartyId WHERE preferredOrderEnumId = SpoMain

</td></tr><tr><td>stocAchCuTVA</td><td>product.stocLX \* lastBuyingPriceWithVAT</td></tr><tr><td>DIO</td><td>stocAchCuTVA / dailyCogs

dailyCogs = (Product.getTotalSalesPeUltimulAn - Product.getTotalProfitPeUltimulAn) / 250(zile lucratoare in an)

</td></tr><tr><td>stocMinim</td><td>PF.minimumStock

ProductFacility PF where PF.facilityId = gest.id AND PF.productId = product.id

</td></tr><tr><td>For each gestiune</td><td> </td></tr><tr><td>stoc gest.getImportName</td><td>p.stoc(gest)</td></tr><tr><td> </td><td> </td></tr><tr><td>comanda recomandată L2</td><td>PF.minimumStock-p.stoc(gest) ?. p.recommendedOrder(gest)</td></tr><tr><td>comenzi furnizor L2</td><td>SUM(R.quantity) mantle.request.requirement.Requirements R WHERE R.productId = product.id

</td></tr><tr><td>adauga</td><td>new Requirement with statusId = RqmtStCreated</td></tr></tbody></table>

### Aproba

<table border="1" id="bkmrk-table-requirements-r"><tbody><tr><td>**Table**</td><td>**Requirements R** WHERE R.statusId = RqmtStCreated AND R.facilityId = L2  
**JOIN Product P  
JOIN ProductPrice PP**

</td></tr><tr><td>Cod</td><td>P.BARCODE\_FIELD</td></tr><tr><td>Denumire</td><td>P.NAME\_FIELD</td></tr><tr><td>UM</td><td>P.UOM\_FIELD</td></tr><tr><td>ULPfTVA</td><td>P.LAST\_BUYING\_PRICE\_FIELD</td></tr><tr><td>preț vânzare</td><td>P.PRICE\_FIELD</td></tr><tr><td>Furnizori</td><td>**Organization.organizationNames** from PP.vendorPartyId  
ORDER BY PP.preferredOrderEnumId, PP.price

</td></tr><tr><td>Necesar</td><td>R.quantity</td></tr></tbody></table>

<table border="1" id="bkmrk-comanda-popup-select"><tbody><tr><td>**Comanda popup**</td><td>**Selected rows R**

</td></tr><tr><td>furnizor(**S**)</td><td>Auto select most common supplier from selected rows</td></tr><tr><td>trimite pe</td><td>Update on Supplier change  
Printeaza, ${PartyContactMech PCM WHERE PCM.contactMechPurposeId = 'PhoneShippingOrigin'},  
IF supplier is internal organization THEN Transfera

</td></tr></tbody></table>

### Comenzi

<table border="1" id="bkmrk-table-requirements-r-1"><tbody><tr><td>**Table**</td><td>**Requirements R** WHERE R.statusId = RqmtStOrdered AND R.facilityId = L2  
**JOIN Product P**

</td></tr><tr><td>Cod</td><td>P.BARCODE\_FIELD</td></tr><tr><td>Denumire</td><td>P.NAME\_FIELD</td></tr><tr><td>UM</td><td>P.UOM\_FIELD</td></tr><tr><td>Furnizor</td><td>R.description</td></tr><tr><td>Comandat</td><td>R.quantity</td></tr></tbody></table>

# Managerul de magazin creează produsele

## Data mapping

Prices are stored in mantle.product.ProductPrice, using the following fields:

- **productPriceId**: autoincrement
- **productId**: productId
- **pricePurposeEnumId**: "PppPurchase"
- **priceUomId**: "RON"
- **priceTypeEnumId**: "PptList" or "PptCurrent"
- **price**: price

## Data statements

- Regular price is **ProductPrice** of type **PptList.**
- Promotional price is **ProductPrice** of type **PptCurrent.**
- Quantity promotion is **ProductPrice** of type **PptCurrent** with extra field: **minQuantity.**
- Customer specific pricing is **ProductPrice** of type **PptCurrent** with extra field: **customerPartyId.**
- Customer specific quantity promotion is **ProductPrice** of type **PptCurrent** with extra fields: **minQuantity, customerPartyId.**

# Managerul de magazin recepționează factura

Când **Managerul de magazin** inregistrează o Factură de intrare si produsul este Marfa, **Sistemul** actualizează prețul furnizorului și îl setează ca furnizor principal pentru marfa recepționată.  
Când **Managerul de magazin** înregistrează o linie de intrare și cantitatea e mai mare ca 0, **Sistemul** șterge comenzile puse pentru produsul respectiv.

## Managerul receptioneaza eFactura

### Povestea

**Sistemul** descarca facturile primite de pe ANAF. **Managerul** incarca eFacturile de pe o anumita perioada, apoi selecteaza o factura care nu a fost receptionata. **Sistemul** gaseste partenerul dupa CUI si il selecteaza. **Managerul** face receptia cu unul din modurile:

1. **Receptioneaza**. **Sistemul** deschide fereastra Manager -&gt; Receptii si precompleteaza campurile cu datele de pe factura, apoi deschide dialogul Adauga si precompleteaza campurile cu datele de la prima linie de pe factura. Dupa ce **Managerul** salveaza linia, **Sistemul** actualizeaza codul furnizorului pentru produsul respectiv, marcheaza linia de pe eFactura ca receptionata si continua cu urmatoarele linii de pe factura. Dupa ce ultima linie a fost receptionata, **Sistemul** marcheaza eFactura ca receptionata.
2. **Fara linii**. **Sistemul** deschide fereastra Urmarire Parteneri, selecteaza partenerul de pe factura si modul CUMPARARE, apoi deschide Adauga Document si precompleteaza campurile. Dupa ce **Managerul** salveaza factura, **Sistemul** marcheaza eFactura ca receptionata.
3. **Doar marcheaza**. **Sistemul** marcheza eFactura ca receptionata.

### Data mapping

A received ANAF eInvoice is stored as:

**moqui.service.message.SystemMessage**

- **systemMessageId:** AnafReceivedMessage.id
- **systemMessageTypeId**: ANAFReceivedInvoice
- **statusId**: SmsgReceived
- **isOutgoing**: N
- **initDate**: now
- **processedDate**: confirmed date
- **messageText**: rawXml
- **senderId**: AccountingSupplierParty.Party.PartyTaxScheme.CompanyID
- **receiverId**: AccountingCustomerParty.Party.PartyTaxScheme.CompanyID
- **messageId**: cbc:ID(invoice number)
- **messageDate**: IssueDate
- **docType**: "Invoice" or "CreditNote"
- **docSubType**: InvoiceTypeCode or CreditNoteTypeCode
- **docControl**: LegalMonetaryTotal.TaxInclusiveAmount.value

**moqui.service.message.SystemMessageType**

- **systemMessageTypeId**: ANAFReceivedInvoice
- **description**: "Invoice received through a message from ANAF eFactura"

**mantle.account.invoice.InvoiceSystemMessage**

- **invoiceId**: AccountingDocument.id
- **systemMessageId**: AnafReceivedMessage.id

And the invoice lines are split into one message each:

**moqui.service.message.SystemMessage**

- **systemMessageId:** AnafReceivedMessage.id+"\_"+InvoiceLine.id
- **systemMessageTypeId**: ANAFReceivedInvoiceLine
- **statusId**: SmsgReceived
- **isOutgoing**: N
- **initDate**: now
- **processedDate**: confirmed date
- **parentMessageId**: AnafReceivedMessage.id
- **messageText**: InvoiceLineXml

**moqui.service.message.SystemMessageType**

- **systemMessageTypeId**: ANAFReceivedInvoiceLine
- **description**: "Invoice line split from an ANAF eInvoice"

### Data statements

- A received(posted in the books) ANAF eInvoice is **SystemMessage** with **statusId**=SmsgConfirmed.
- A received ANAF eInvoice line is a **SystemMessage** with **statusId**=SmsgConfirmed

### Service data mapping

#### **Managerul** incarca eFacturile de pe o anumita perioada

**Managerul** interogheaza **Moqui** pe o perioada intre from si to.

**Moqui** interogheaza **cloud-anaf-connector** la /invoices/search/between (from, to). **Cloud-anaf-connector** returneaza List&lt;ReceivedInvoice&gt;. Pentru fiecare ReceivedInvoice.id=systemMessageId care nu exista **Moqui** creeaza un SystemMessage. **Moqui** consuma SystemMessages noi.

**Moqui** returneaza **Managerului** efacturile intre from si to.

#### **find#AnafInvoices**

SystemMessage SM  
JOIN InvoiceSystemMessage ISM on systemMessageId  
WHERE SM.systemMessageTypeId=ANAFReceivedInvoice AND SM.messageDate between(from, to)

ReceivedMessage RM  
WHERE RM.creationDate between(from, to)

IN

- **start**: start
- **end**: end

OUT

- **id**: SM.systemMessageId ?: RM.id
- **senderId**: SM.senderId ?: RM.taxId
- **senderName**: AccountingSupplierParty.Party.PartyLegalEntity.RegistrationName
- **issueDate**: SM.messageDate :? RM.creationDate
- **invoiceNumber**: SM.messageId
- **invoiceTotal**: LegalMonetaryTotal.TaxInclusiveAmount.value
- **taxTotal**: TaxTotal.TaxAmount.value
- **taxExclusiveAmount**: LegalMonetaryTotal.TaxExclusiveAmount.value
- **invoiceId**: ISM.invoiceId
- **statusId**: SM.statusId
- **rawXml**: messageText
- **messageType**: "AnafRecMsgBillReceived" ?: RM.statusId
- **messageTypeL10n**: ec.l10n.localize("AnafRecMsgBillReceived") ?: ec.l10n.localize(RM.statusId)
- **details**: RM.details

#### **find#AnafInvoiceLines**

SystemMessage SML WHERE SML.systemMessageTypeId=ANAFReceivedInvoiceLine AND SML.parentMessageId=systemMessageId

IN

- **systemMessageId**: systemMessageId

OUT

- **id**: SML.systemMessageId
- **lineId**: ID
- **itemId**: Item.SellersItemIdentification.ID
- **name**: Item.Name + Item.Description
- **price**: Price.PriceAmount
- **priceCurrency**: Price.PriceAmount(currencyID)
- **quantity**: InvoicedQuantity or CreditedQuantity
- **uom**: <span class="wikiexternallink">[UNECERec20ToDisplay](https://docs.peppol.eu/logistics/codelist/UNECERec20/)</span>(InvoicedQuantity(unitCode) or CreditedQuantity(unitCode))
- **total**: LineExtensionAmount
- **totalCurrency**: LineExtensionAmount(currencyID)
- **statusId**: SML.statusId

### <span style="color: inherit; font-family: inherit; font-size: max(23px, min(29px, 12.3333px + 1.38889vw));">Screen data mapping</span>

<table id="bkmrk-table-find%23anafinvoi"><tbody><tr><td>**Table**</td><td>find#anafInvoices</td></tr><tr><td>Id</td><td>id</td></tr><tr><td>Cif emitent</td><td>senderId</td></tr><tr><td>Nume emitent</td><td>senderName

</td></tr><tr><td>Tip mesaj</td><td>messageTypeL10n

</td></tr><tr><td>Data</td><td>issueDate</td></tr><tr><td>Mesaj</td><td>details</td></tr><tr><td>Numar</td><td>invoiceNumber</td></tr><tr><td>ValCuTVA</td><td>invoiceTotal</td></tr><tr><td>Id factura</td><td>invoiceId</td></tr><tr><td>Receptionat</td><td>**true** if statusId=SmsgConfirmed else **false**</td></tr></tbody></table>

<table id="bkmrk-receptie-popup-selec"><tbody><tr><td>**Receptie popup**</td><td>selected AnafInvoice AI FILTER AI.messageType = "AnafRecMsgBillReceived"</td></tr><tr><td>Partener</td><td>Partner P WHERE P.codFiscal=AI.senderId</td></tr><tr><td>Factura</td><td>AI.invoiceNumber</td></tr><tr><td>Data</td><td>AI.issueDate</td></tr><tr><td>ValFaraTVA</td><td>AI.taxExclusiveAmount</td></tr><tr><td>ValTVA</td><td>AI.taxTotal</td></tr><tr><td>ValCuTVA</td><td>AI.invoiceTotal</td></tr><tr><td>lines summary</td><td>foreach find#AnafInvoiceLines IL

IL.lineId IL.name IL.price IL.priceCurrency X IL.quantity uom = IL.total IL.totalCurrency

</td></tr></tbody></table>

# Managerul reconciliază extrasul bancar

## Data mapping

foreach line WHERE line = debit

- **IF** line starts with ignore case 'COMIS' **THEN** doc.gestiune=gestiuneId | Partner='RAIFF gestiune.name.upper' | TipDoc.PLATA | doc=CARD | docNr=NC | date=closingDate | doc.name=COM | doc.total=sum(lines) | banca=contBancarId
- **ELSE IF** line.partner = 'LINIC SRL' **THEN** doc.gestiune=L1 | Partner='RAIFF gestiune.name.upper' | TipDoc.PLATA | doc='ORDIN PLATA' | docNr=OP | date=line.date | doc.name=line.description | doc.total=line.total | banca=contBancarId
- **ELSE** Partner=line.partner | TipDoc.PLATA | doc='ORDIN PLATA' | date=line.date | doc.total=line.total | banca=contBancarId

foreach line WHERE line = credit

- **IF** line contains 'Depunere numerar' **THEN** doc.gestiune=gestiuneId | Partner='RAIFF gestiune.name.upper' | TipDoc.INCASARE | doc='CHITANTA' | date=line.date | doc.name=INC | doc.total=line.total | banca=contBancarId
- **ELSE IF** line starts with 'AK' AND contains 'POS' **THEN** doc.gestiune=gestiuneId | Partner='CARD INCASARE' | TipDoc.INCASARE | doc='CARD' | docNr='NC' | date=line.date | doc.name='INC' | doc.total=line.total | banca=contBancarId **OR** doc.gestiune=gestiuneId | Partner not 'CARD INCASARE' or 'RAIFF...' | TipDoc.INCASARE | doc='CARD' | date=line.date | doc.total=line.total | banca=contBancarId

# Vânzătorul vinde marfa

Cand **Vanzatorul** întregistrează o linie de vânzare în interfața nouă și prețul din Moqui este diferit, **Sistemul** adaugă diferența de preț ca și o linie nouă de discount.

Cand **Vanzatorul** întregistrează o linie de vânzare în interfața nouă **și** închide bonul **și** furnizorul principal al produsului este Dunca **și** stocul intră în negativ, **Sistemul** trimite comandă la furnizor pentru produsul respectiv.

## Data statements

A POS sale is an OrderHeader with **salesChannelEnumId =** **POS** and Customer = **\_NA\_** and **CarrierShipmentMethod.shipmentMethodEnumId = ShMthPickUp**.

A POS cash payment is represented by an **Invoice** of type **InvoiceSimplified** with an attached payment of type **PtInvoicePayment** and **paymentInstrumentEnumId** = PiCash OR PiCod and **toPaymentMethodId** is a PaymentMethod with **paymentMethodTypeEnumId** = PmtCash and **ownerPartyId** = facility/store party id.

A POS card payment is represented by an **Invoice** of type **InvoiceSimplified** with an attached payment of type **PtInvoicePayment** and **paymentInstrumentEnumId** = PiDebitCard and **toPaymentMethodId** is a PaymentMethod with **paymentMethodTypeEnumId** = PmtBankAccount and **ownerPartyId** = facility/store party id, and an attached BankAccount(subtype of PaymentMethod).

NOTE: we use BankAccount for pos card payments because the money goes directly to our BankAccount and we don't store card details. The CreditCard payment method subtype with the type PmtCreditCard is only used for online card payments, where we need the card details.

```
Scenario A: Customer pays an invoice using store credit  
  paymentTypeEnumId    = PtInvoicePayment  
  paymentInstrumentEnumId = PiFinancialAccount  
  paymentMethodId      → PaymentMethod (PmtFinancialAccount)  
                              └─ finAccountId → FinancialAccount (CustomerCredit)  
  Payment.finAccountId → same FinancialAccount  
  finAccountAuthId     → FinancialAccountAuth (holds the funds)  
  finAccountTransId    → FinancialAccountTrans (the debit from the account)  
  
Scenario B: Loading money onto a gift card (funding the FinancialAccount)  
  paymentTypeEnumId    = PtFinancialAccount  
  paymentInstrumentEnumId = PiCreditCard  (how the customer pays to load it)  
  paymentMethodId      → PaymentMethod (PmtCreditCard)  
  Payment.finAccountId → FinancialAccount (GiftCard) being loaded  
```

# Import data from legacy app

## Import Partners

### Data mapping

Company company;  
private Delegat delegat;  
Integer termenPlata;  
List&lt;Document&gt; documents;  
Boolean platitorTva;  
Boolean tvaLaIncasare;  
LocalDate dataInceputTvaInc;  
LocalDate dataSfarsitTvaInc;  
Boolean splitTva;  
LocalDate dataInceputSplitTVA;  
LocalDate dataAnulareSplitTVA;  
FidelityCard fidelityCard;  
Boolean inactiv;  
Set&lt;PartnerGrupaInteresMapping&gt; grupeInteres = new HashSet();  
boolean notifyAppointment = false;

Add Customer and Supplier role

```
mantle.party.Party
partyId = id
  isPerson = isEmpty(legacyP.getCodFiscal())
partyTypeEnumId = isPerson ? "PtyPerson" : "PtyOrganization"
disabled = !activ

mantle.party.Person
firstName = name.remove("^A - ").split(" ")[1+" "+..i]
lastName = name.remove("^A - ").split(" ")[0]
nickname = name

mantle.party.Organization
organizationName = name

mantle.party.PartyIdentification
partyIdTypeEnumId = "PtidTaxId"
idValue = codFiscal

mantle.party.PartyIdentification
partyIdTypeEnumId = "PtidTradeReg"
idValue = regCom

mantle.party.contact.ContactMech
contactMechId = legacyId + "_PHONE"
contactMechTypeEnumId = "CmtTelecomNumber"
mantle.party.contact.TelecomNumber
contactNumber = phone
mantle.party.contact.PartyContactMech
contactMechPurposeId = "PhonePrimary"
fromDate = y2000

mantle.party.contact.ContactMech
contactMechId = legacyId + "_EMAIL"
contactMechTypeEnumId = "CmtEmailAddress"
infoString = email
mantle.party.contact.PartyContactMech
contactMechPurposeId = "EmailPrimary"
fromDate = y2000

mantle.party.contact.ContactMech
contactMechId = legacyId + "_ADDR"
contactMechTypeEnumId = "CmtPostalAddress"
mantle.party.contact.PostalAddress
countryGeoId = legacyAddr.getCountry()
countyGeoId = legacyAddr.getJudet()
city = legacyAddr.getOras()
address1 = legacyAddr.getStrada()
postalCode = legacyAddr.getNr()
mantle.party.contact.PartyContactMech
contactMechPurposeId = "PostalPrimary"
fromDate = y2000

mantle.party.contact.ContactMech
contactMechId = legacyId + "_DELIV_ADDR"
contactMechTypeEnumId = "CmtPostalAddress"
mantle.party.contact.PostalAddress
address1 = deliveryAddress
directions = indicatii
mantle.party.contact.PartyContactMech
contactMechPurposeId = "PostalShippingDest"
fromDate = y2000

mantle.account.method.PaymentMethod
paymentMethodId = legacyId + "_BANK"
paymentMethodTypeEnumId = "PmtBankAccount"
ownerPartyId = legacyId
mantle.account.method.BankAccount
bankName = banca
accountNumber = iban
```

# Programul de fidelizare Partener Colibri

## Data mapping

A Colibri Partner is:

```
mantle.party.PartyRole
partyId = partner.id
roleTypeId = "Affiliate"

mantle.party.PartyIdentification
partyId = partner.id
partyIdTypeEnumId = "PtidAffiliateId"
idValue = partnerCode

mantle.party.contact.ContactMech
contactMechId = partner.id + "_PHONE"
contactMechTypeEnumId = "CmtTelecomNumber"
mantle.party.contact.TelecomNumber
contactMechId = contactMechId
contactNumber = phone
mantle.party.contact.PartyContactMech
partyId = partner.id
contactMechId = contactMechId
contactMechPurposeId = "PhonePrimary"
fromDate = now

mantle.account.financial.FinancialAccount
finAccountId = partner.id
finAccountTypeId = "ServiceCredit"
statusId = "FaActive"
finAccountName = "Partener Colibri"
organizationPartyId = L2
ownerPartyId = partner.id

mantle.party.agreement.AgreementParty
agreementId = "PartenerColibri"
partyId = partner.id
roleTypeId = "Affiliate"

mantle.party.agreement.Agreement
agreementId = "PartenerColibri"
agreementTypeEnumId = "AgrCommission"
organizationPartyId = L2
organizationRoleTypeId = "OrgInternal"

mantle.party.agreement.AgreementTerm
agreementTermId = "PartenerColibri_1..10"
agreementId = "PartenerColibri"
termTypeEnumId = "TtCommission"
termNumber = 50,150,300,500,750,1500,2500,3500,6000,10000
minQuantity = 500,5000,10000,15000,20000,30000,40000,50000,75000,100000
```

A received payment from a Colibri partner is:

```
mantle.account.payment.Payment
paymentId = legacyIncasare.id
paymentTypeEnumId = "PtInvoicePayment"
fromPartyId = legacyIncasare.partner.id
toPartyId = L2
statusId = "PmntDelivered"
effectiveDate = legacyIncasare.dataDoc
amount = legacyIncasare.getTotal()

mantle.account.payment.PaymentParty
paymentId = paymentId
partyId = affiliatePartnerId
roleTypeId = "Affiliate"
```

When a new threshold is reached the customer credit is replenished like this:

```
mantle.account.financial.FinancialAccountTrans
finAccountTransId = "PC${partner.id}_1..10"
finAccountTransTypeEnumId = "FattDeposit"
finAccountId = partner.id
fromPartyId = L2
toPartyId = partnerAffiliate.id
transactionDate = now
amount = termNumber - previous termNumber
```

## Service mapping

```
WHEN
foreach incaseazaDocsOrPartner().paidDocs AS legacyIncasare
closeBonCasa().get(InvocationResult.ACCT_DOC_KEY) AS legacyIncasare
closeFacturaBCAviz().get(InvocationResult.CHITANTA_KEY) AS legacyIncasare
THEN
createPayments()

seca: checkPartnerAgreements after createPayments()
affiliatePartyId = PaymentParty.partyId WHERE PaymentParty.roleTypeId = "Affiliate"
if null affiliatePartyId = Payment.fromPartyId
agreementParty = AgreementParty WHERE agreementId = "PartenerColibri" AND roleTypeId = "Affiliate" AND partyId = affiliatePartyId
if agreementParty is null then EXIT
lastThresholdReached = FinancialAccountTrans WHERE toPartyId = affiliatePartyId AND finAccountTransTypeEnumId = "FattDeposit" AND fromPartyId = "L2" AND finAccountId = affiliatePartyId ORDER BY finAccountTransId.split("_")[1].toInt GET LARGEST
nextThreshold = AgreementTerm WHERE agreementTermId = "PartenerColibri_${lastThresholdIndex + 1}" ?: "PartenerColibri_1"
lastReachedTerm = AgreementTerm WHERE agreementTermId = "PartenerColibri_${lastThresholdIndex}" ?: null
if nextThreshold is null then EXIT
if affiliatePartyPaymentsTotal > nextThreshold.minQuantity AND NOT EXISTS FinancialAccountTrans WHERE finAccountTransId = "PC${partner.id}_${lastThresholdIndex + 1}"  
  create FinancialAccountTrans
    finAccountTransId = "PC${affiliatePartyId}_${lastThresholdIndex + 1}"
    finAccountTransTypeEnumId = "FattDeposit"
    finAccountId = partner.id
    fromPartyId = "L2"
    toPartyId = affiliatePartyId
    transactionDate = now
    amount = nextThreshold.termNumber - (lastReachedTerm.termNumber ?: 0)
  create legacy DiscountDoc incasare
  send threshold reached SMS to affiliate
else
  send payment received SMS to affiliate
```