📓 Colibri

Scopul

Viziunea 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.

Misiunea curentă este să o facem profitabilă.

Rezultate cheie

Indicatori 2025

Indicatori 2026

Resurse

Sarcini

Calculator livrare

Acțiuni

📜 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

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

Comandă marfă

Video

Pașii principali

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

PASUL 2 – Verificarea secțiunii „Aprobă”

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

PASUL 3 – Plasarea comenzii

Cadența

Checklist

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

Comandă marfă - Dunca

Comandă marfă - Transfer

👨‍💼 Managerul de magazin

Managerul de magazin creează produsele

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

Modificare cote TVA

Video

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

Managerul de magazin recepționează factura

Printare Receptie - Grupeaza dupa TVA la achizitie

💰 Vânzătorul

💰 Vânzătorul

Vânzătorul vinde marfa

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

💻 Sistemul

💻 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:

Product Pareto category is stored as:

mantle.product.category.ProductCategoryMember
mantle.product.category.ProductCategory

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

mantle.product.Product
mantle.facility.ProductFacility:

Product ordering requirements are stored as:

mantle.request.requirement.Requirement

Screen outline

Necesar

Aproba

Comenzi

Screen data mapping

Necesar

Table 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
cod Product.BARCODE_FIELD
denumire Product.NAME_FIELD
UM Product.UOM_FIELD

Pareto

mantle.product.category.ProductCategoryMember.productCategoryId
ultimul preț achiziție fără TVA Product.LAST_BUYING_PRICE_FIELD
preț vânzare Product.PRICE_FIELD

furnizori

Organization.organizationName from ProductPrice.vendorPartyId WHERE preferredOrderEnumId = SpoMain

stocAchCuTVA product.stocLX * lastBuyingPriceWithVAT
DIO

stocAchCuTVA / dailyCogs

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

stocMinim

PF.minimumStock

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

For each gestiune  
stoc gest.getImportName p.stoc(gest)
   
comanda recomandată L2 PF.minimumStock-p.stoc(gest) ?. p.recommendedOrder(gest)
comenzi furnizor L2

SUM(R.quantity) mantle.request.requirement.Requirements R WHERE R.productId = product.id

adauga new Requirement with statusId = RqmtStCreated

Aproba

Table

Requirements R WHERE R.statusId = RqmtStCreated AND R.facilityId = L2
JOIN Product P
JOIN ProductPrice PP

Cod P.BARCODE_FIELD
Denumire P.NAME_FIELD
UM P.UOM_FIELD
ULPfTVA P.LAST_BUYING_PRICE_FIELD
preț vânzare P.PRICE_FIELD
Furnizori

Organization.organizationNames from PP.vendorPartyId
ORDER BY PP.preferredOrderEnumId, PP.price

Necesar R.quantity
Comanda popup

Selected rows R

furnizor(S) Auto select most common supplier from selected rows
trimite pe

Update on Supplier change
Printeaza, ${PartyContactMech PCM WHERE PCM.contactMechPurposeId = 'PhoneShippingOrigin'},
IF supplier is internal organization THEN Transfera

Comenzi

Table

Requirements R WHERE R.statusId = RqmtStOrdered AND R.facilityId = L2
JOIN Product P

Cod P.BARCODE_FIELD
Denumire P.NAME_FIELD
UM P.UOM_FIELD
Furnizor R.description
Comandat R.quantity
💻 Sistemul

Managerul de magazin creează produsele

Data mapping

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

Data statements

💻 Sistemul

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 -> 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

moqui.service.message.SystemMessageType

mantle.account.invoice.InvoiceSystemMessage

And the invoice lines are split into one message each:

moqui.service.message.SystemMessage

moqui.service.message.SystemMessageType

Data statements

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<ReceivedInvoice>. 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)

IN

OUT

find#AnafInvoiceLines

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

IN

OUT

Screen data mapping

Table find#anafInvoices
id id
cif emitent senderId
nume emitent

senderName

data issueDate
numar invoiceNumber
ValCuTVA invoiceTotal
id factura invoiceId
receptionat true if statusId=SmsgConfirmed else false
Receptie popup selected AnafInvoice AI
Partener Partner P WHERE P.codFiscal=AI.senderId
Factura AI.invoiceNumber
Data AI.issueDate
ValFaraTVA AI.taxExclusiveAmount
ValTVA AI.taxTotal
ValCuTVA AI.invoiceTotal
lines summary

foreach find#AnafInvoiceLines IL

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

💻 Sistemul

Managerul reconciliază extrasul bancar

Data mapping

foreach line WHERE line = debit

foreach line WHERE line = credit

💻 Sistemul

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  
💻 Sistemul

Import data from legacy app

Import Partners

Data mapping

Company company;
private Delegat delegat;
Integer termenPlata;
List<Document> documents;
Boolean platitorTva;
Boolean tvaLaIncasare;
LocalDate dataInceputTvaInc;
LocalDate dataSfarsitTvaInc;
Boolean splitTva;
LocalDate dataInceputSplitTVA;
LocalDate dataAnulareSplitTVA;
FidelityCard fidelityCard;
Boolean inactiv;
Set<PartnerGrupaInteresMapping> 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

💻 Sistemul

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