Skip to Content

Odoo 19 Community. հաշիվ-սևագրերի միավորում՝ մեր բաց կոդով Merge Draft Invoices մոդուլը

18 սեպտեմբերի, 2026 թ. by
Odoo 19 Community. հաշիվ-սևագրերի միավորում՝ մեր բաց կոդով Merge Draft Invoices մոդուլը
Alexander Biringer | Biricon IT Services GmbH

Ով Odoo-ում հաշվարկում է պատվերներից, Helpdesk տիկետներից և ժամանակի հաշվառումից, ծանոթ է արդյունքին. հաճախորդն ամսվա վերջին ստանում է ոչ թե մեկ հաշիվ, այլ հինգ։ Յուրաքանչյուրը ծագում է տարբեր պատվերից կամ տիկետից, յուրաքանչյուրը բովանդակային առումով ճիշտ է, և այնուամենայնիվ հաճախորդը հարցնում է, թե չի՞ կարող դա տեղավորվել մեկ փաստաթղթի մեջ։

Հենց դրա համար Odoo 19 Community-ում մաքուր լուծում չկա։ Հայտնի OCA account_invoice_merge մոդուլը Odoo 19-ի համար դեռ թույլատրված չէ, և այն խնդիրը լուծում է այնպես, որ ծառայություններ մատուցողների համար լուրջ ռիսկ է ներկայացնում՝ նույն դիրքերը միավորվում են, և դրանով կորչում է կապը պատվերի, առաջադրանքի և գրանցված ժամանակի հետ։

Ուստի մեր սեփական Odoo 19 Community տեղադրման համար մենք մշակել ենք Merge Draft Invoices մոդուլը։ Քանի որ այս խնդիրը վերաբերում է ոչ միայն մեզ, այն անվճար տրամադրում ենք GitHub-ում՝ ազատ AGPL-3 լիցենզիայով։

Նախագծին GitHub-ում՝

Odoo 19 Merge Draft Invoices

Ինչու առանձին մոդուլ՝ OCA-ի լուծման փոխարեն

OCA-ն շատ տարիներ պահպանում է account_invoice_merge-ը OCA/account-invoicing repository-ում։ Այս հոդվածի պահին 19.0-ին միգրացիան այնտեղ դեռ բաց Pull Request է, ոչ թե հրապարակված մոդուլ։

Սակայն ավելի կարևորն այն է, որ երկու մոդուլները տարբեր բաներ են անում՝


OCA account_invoice_mergeMerge Draft Invoices
Արդյունքմիավորվում է ընտրված հաշիվներից մեկի մեջմիշտ նոր հաշիվ
Դիրքերնույն դիրքերը միավորվում են, քանակները գումարվում ենյուրաքանչյուր դիրք փոխանցվում է մեկ առ մեկ
Ծագումinvoice_origin / payment_reference-ը վերնագրում միացվում են իրարծագման դաշտերը յուրաքանչյուր դիրքի համար պատճենվում են Odoo-ի սեփական _copy_data_extend_business_fields hook-ի միջոցով
Ժամանակի հաշվառումհաշվի չի առնվումtimesheet_invoice_id-ը վերակցվում է նոր հաշվին
Ընթեռնելի հաճախորդի համարոչինչվերնագիր յուրաքանչյուր աղբյուրի դիրքերի վերևում՝ պատվերով և տիկետով
Աղբյուր հաշիվներչեղարկվում ենչեղարկել (լռելյայն), ջնջել կամ պահել՝ հստակ անվանված հետևանքներով

Նույն դիրքերի միավորումը որոշիչ տարբերությունն է։ Միավորված դիրքը կարող է կրել միայն մեկ ծագում։ Հենց երկու տարբեր պատվերների երկու տող նույն տեսքն ունեն, կապը հաշվից դեպի պատվեր, առաջադրանք և ժամանակի գրանցում կորչում է։ Ծառայությունների ոլորտի ընկերությունում, որտեղ հաշվարկը գնում է տիկետ → ժամանակի հաշվառում → հաշիվ շղթայով, դա նշանակում է՝ նույն ժամերը կա՛մ երկու անգամ են հաշվարկվում, կա՛մ անհետանում են։

Ուստի մեր մոդուլը սկզբունքորեն ոչինչ չի միավորում։ Միավորված հաշիվն ունի ճիշտ այնքան դիրք, որքան իր աղբյուրները միասին։

OCA մոդուլից կոդ չի վերցվել։ Երկու մոդուլներն էլ AGPL-3 լիցենզիայով են։

Ինչ է անում մոդուլը

Հաշիվների ցանկի տեսքում նշել երկու կամ ավելի սևագիր, գործողությունների մենյուում ընտրել Միավորել հաշիվ-սևագրերը. օգնականը ցույց է տալիս ընտրությունը, ստուգումները և աղբյուր հաշիվների տարբերակները։ Հաստատելուց հետո անմիջապես հայտնվում եք նոր հաշվի վրա։

Միավորելուց առաջ մոդուլը ստուգում է, այն էլ բոլոր կետերը միանգամից, որպեսզի յուրաքանչյուր ուղղումից հետո հաջորդ սխալը չտեսնեք՝

  • ընտրված է առնվազն երկու հաշիվ
  • բոլորը Սևագիր կարգավիճակում են
  • ոչ մեկը երբևէ չի գրանցվել (սևագրի վերադարձված փաստաթուղթը պահպանում է գրանցման պատմությունը և մերժվում է)
  • ոչ մեկն արդեն չի միավորվել
  • բոլորը հաշիվ, մատակարարի հաշիվ, կրեդիտ-նոտա կամ անդորրագիր են՝ ոչ թե զուտ գրանցման ձևակերպումներ
  • նույն գործընկերը, փաստաթղթի տեսակը, ընկերությունը, արժույթը և journal-ը

Վճարման պայմանների, հարկերի համապատասխանեցման, առաքման հասցեի, բանկային հաշվի կամ վաճառողի տարբերությունները չեն արգելափակում։ Օգնականը զգուշացնում է և վերցնում է ընտրված ամենահին հաշվի արժեքը։

Ինչ է պահպանվում

Մոդուլի առանցքը հետևյալն է. հաշվի դիրքի՝ իր ծագման հետ ունեցած յուրաքանչյուր կապ գոյություն ունի նաև նոր հաշվի վրա՝

ԿապՎերաբերմունք
Վաճառքի պատվերի դիրքեր (sale_line_ids)յուրաքանչյուր դիրքի համար պատճենվում է Odoo-ի _copy_data_extend_business_fields-ի միջոցով
Գնումների պատվերի դիրքեր (purchase_line_id)նույն կերպ
Ծախսային կենտրոնների բաշխում (analytic_distribution)պատճենվում է յուրաքանչյուր դիրքի համար
այլ ծագման դաշտեր ըստ դիրքիավտոմատ, եթե մատակարարող մոդուլն իրականացնում է փաստաթղթավորված Odoo hook-ը
Ծագում և հղում հաշվի վերնագրումմիավորվում է, դուբլիկատները հեռացվում են, կարելի է խմբագրել օգնականում
Վաճառքի պատվեր՝ invoice_ids, invoice_status, qty_invoicedհաշվարկվում են դիրքերից՝ պատվերն ինքն է նկատում նոր հաշիվը
Ժամանակի հաշվառում (timesheet_invoice_id)վերակցվում է նոր հաշվին, նախքան աղբյուրները անվավեր դառնալը
Helpdesk-տիկետկապված է ժամանակի հաշվառման հետ և դրանով ավտոմատ ցույց է տալիս նոր հաշվին
Chatterհաղորդագրություն նոր հաշվի վրա՝ բոլոր աղբյուրներով, հաղորդագրություն յուրաքանչյուր աղբյուրի վրա՝ նորի հղումով
merged_into_move_id / merged_from_move_idsպահպանված կապ երկու ուղղությամբ

Հարկային տողերը, վճարման պայմանների տողերը, կլորացման և սկոնտոյի տողերը չեն պատճենվում, այլ Odoo-ի կողմից բովանդակությունից նորից հաշվարկվում են։ Այսպիսով, միավորված հաշիվն ունի մեկ հարկային տող յուրաքանչյուր հարկադրույքի համար, ոչ թե մեկ յուրաքանչյուր աղբյուրի համար։ Այն ստեղծվում է առանց համարի՝ որպես սևագիր. միավորումն ինքնին փաստաթղթի համար չի ծախսում։

Վերնագրեր. հաճախորդը տեսնում է, թե որտեղից է յուրաքանչյուր դիրքը

Սա այն գործառույթն է, որը առօրյայում տարբերություն է ստեղծում։ Յուրաքանչյուր աղբյուր հաշվի դիրքերի վերևում մոդուլը տեղադրում է բաժնի տող՝ այն նույն տարրը, որը Odoo-ն ինքն է օգտագործում «Կանխավճարներ» բաժնի համար՝

BE10181                                     <- Վերնագիր
  22,500 x Տեխնիկի ժամ            3.577,50
                    Միջանկյալ գումար 3.577,50
BE10182 · Ticket: HT00219                   <- Վերնագիր
   3,000 x Տեխնիկի ժամ              477,00
                    Միջանկյալ գումար  477,00
                      ՆԵՏՈ ԳՈՒՄԱՐ 4.054,50

Վերնագիրը տպվում է հաճախորդին տրվող փաստաթղթի վրա, և Odoo-ի ստանդարտ հաշվի հաշվետվությունն ինքնուրույն ավելացնում է միջանկյալ գումար յուրաքանչյուր բաժնի համար։ Այդպիսով հաճախորդը տեսնում է նաև, թե որքան է արժեցել յուրաքանչյուր մասը։

Մակագրությունը կազմվում է այն բանից, ինչ աղբյուրն իրականում ունի՝

  1. պատվերները՝ ուղղակիորեն դիրքերի կապերից. անունները այդպես ճշգրիտ են. եթե պատվերի կապերը բացակայում են, օգտագործվում է invoice_origin-ի ազատ տեքստը
  2. տիկետները՝ որոշվում են ժամանակի հաշվառման միջոցով. ցուցադրվում են որպես Ticket: HT00219 կամ Tickets: …, սահմանափակված հինգով՝ գումարած «և ևս N»
  3. եթե երկուսն էլ չկան՝ աղբյուր հաշվի համարը, հետո նրա հղումը
  4. եթե ոչինչ չկա՝ չեզոք «Այլ դիրքեր», որպեսզի մեկ աղբյուրի տողերը երբեք լուռ չանհետանան վերևի բլոկում

Այն, ինչ բացակայում է, բաց է թողնվում, չի հորինվում։ Տիկետը տպվում է իր սեփական համարով՝ helpdesk_mgmt-ի համար, Enterprise-ի ticket_ref կամ մոդելի ցուցադրվող անունը՝ երբեք տվյալների բազայի ID-ով։

Վերնագրերը գրվում են հաճախորդի լեզվով, ինչպես Odoo-ն անում է պատվերներից հաշիվներ ստեղծելիս։ Chatter-ը մնում է օգտատիրոջ լեզվով։

Վերնագիրը չի կրում ո՛չ ապրանք, ո՛չ քանակ, ո՛չ գին, ո՛չ հարկ, ո՛չ հաշիվ, ո՛չ ծախսային կենտրոնների բաշխում։ Այն չի կարող ազդել գումարների, հարկային բազայի և ծախսերի հաշվառման վրա։ Ով անջատում է վերնագրերը օգնականում, ստանում է ճիշտ այն հաշիվը, որը կստեղծվեր առանց այս գործառույթի. թեստը համեմատում է երկու արդյունքները մինչև ցենտը։

Ինչ է լինում աղբյուր հաշիվների հետ

Օգնականն առաջարկում է երեք տարբերակ՝

  • Չեղարկել (լռելյայն, խորհուրդ է տրվում)։ Սևագրի չեղարկումը հետևանքներ չունի. ո՛չ փաստաթղթի համար, ո՛չ գրանցում։ Հաշիվները մնում են տվյալների բազայում որպես չեղարկված փաստաթղթեր՝ Chatter-ով և նոր հաշվի հղումով։ Վաճառքի պատվերի qty_invoiced-ը բաց է թողնում չեղարկված փաստաթղթերը, ոչինչ երկու անգամ չի հաշվվում։ Սա այն տարբերակն է, որը նույնիսկ վեց ամիս անց դեռ հետևելի է։
  • Ջնջել։ Անդառնալի։ Chatter-ը և հետադարձ հղումը մահանում են գրառման հետ. մնում է միայն հաղորդագրությունը նոր հաշվի վրա։ Իմաստալից է մեկանգամյա սևագրերի համար, որոնք ոչ ոք այլևս չպետք է տեսնի։
  • Թողնել որպես սևագիր։ Աղբյուրները մնում են բաց։ Դրանց վաճառքի պատվերները այդ դեպքում աղբյուրը և նոր հաշիվը հաշվում են որպես հաշվարկված. աշխատանքը կարծես երկու անգամ է հաշվարկված, մինչև որևէ մեկը սևագրերը ձեռքով մաքրի։ Նախատեսված է հազվադեպ դեպքի համար, երբ որոշումից առաջ ցանկանում են համեմատել։ Թեստը հենց այս կրկնակի հաշվարկն է արձանագրում, որպեսզի այն ոչ ոքի չզարմացնի։

Սահմանափակումներ. ինչ չի անում մոդուլը միտումնավոր

Խնդրում ենք կարդալ արտադրական օգտագործումից առաջ՝

  • Միայն սևագրեր։ Գրանցված հաշիվները չեն միավորվում։ Սա կրեդիտ-նոտայի խնդիր է, ոչ թե պատճենման գործընթաց։
  • Մեկ անգամ գրանցված, վերադարձված սևագրերը մերժվում են։ Դրանք ունեն համար և գրանցման պատմություն. դա մեկ այլ փաստաթղթում թաքցնելը այն չէ, ինչ զանգվածային գործողությունը պետք է լուռ կատարի։
  • Գործընկերների, արժույթների, ընկերությունների կամ journal-ների սահմաններով միավորում չկա։ Դրա համար իմաստալից միասնական արժեք գոյություն չունի, ուստի մոդուլը մերժում է՝ գուշակելու փոխարեն։
  • payment_reference-ը չի փոխանցվում։ Կառուցվածքավորված վճարման հղումները նույնականացնում են մեկ փաստաթուղթ. երկուսը իրար միացված բանկում ոչ մի բանի չեն համապատասխանում։ Նոր հաշիվը գրանցելիս ստանում է իր սեփականը։
  • Կանխավճարի դիրքերը պատճենվում են ինչպես ցանկացած այլ դիրք։ Եթե աղբյուրները պարունակում են sale-ի կանխավճարի տողեր, արդյունքը պետք է ստուգել գրանցելուց առաջ։
  • Միայն timesheet_invoice_id-ն է ավտոմատ վերակցվում։ account.move-ին Many2one ունեցող ցանկացած այլ մոդել (սեփական հաշվարկային անցումներ, բաժանորդագրության կապեր, EDI փոխանցումներ) շարունակում է ցույց տալ աղբյուրին։ Դրա համար կա _merge_draft_relink hook-ը։
  • Նույն դիրքերի միավորում չկա՝ միտումնավոր։ Ով կարճ, կոկիկ հաշիվ է ուզում, սխալ տեղում է և դրա դիմաց կվճարի ծագման կապերով։
  • Վերնագրերը ստեղծվում են միայն մեկ անգամ՝ միավորման ժամանակ։ Դրանից հետո դրանք սովորական բաժնի տողեր են՝ խմբագրելի, ջնջելի և չեն թարմացվում, եթե պատվերը կամ տիկետը հետագայում վերանվանվեն։
  • Վերնագիրը նշում է աղբյուր հաշվի ամբողջ տիկետները, ոչ թե առանձին դիրքինը։

Տեղադրում Odoo 19 Community-ում

Տեղադրումը կատարվում է ինչպես Odoo-ի սովորական հավելյալ մոդուլի դեպքում՝

  1. Պատճենել մոդուլի թղթապանակը կարգավորված addons ուղու մեջ։
  2. Թարմացնել հավելվածների ցանկը Odoo-ում։
  3. Տեղադրել account_invoice_merge_draft մոդուլը։

Մոդուլը կախված է միայն account-ից։ Այն ամենը, ինչ այն գիտի sale-ի, purchase-ի, project-ի, hr_timesheet-ի, sale_timesheet-ի կամ Helpdesk մոդուլի մասին, որոշվում է աշխատանքի ընթացքում. այն տեղադրվում և աշխատում է նաև զուտ հաշվարկային տվյալների բազայում։

Դրանից հետո՝ Հաշվապահություն → Հաճախորդներ → Հաշիվներ (կամ Մատակարարներ → Հաշիվներ), նշել երկու կամ ավելի սևագիր, ատամնանիվի մենյու → Միավորել հաշիվ-սևագրերը։ Գործողությունը հասանելի է Հաշվարկ / Հաշիվների դուրսգրում խմբի բոլոր օգտատերերին։

Ով թարմացնում է մոդուլի նախկին տարբերակից, պետք է մեկ անգամ թարմացնի -u account_invoice_merge_draft --i18n-overwrite հրամանով, որպեսզի փոփոխված դաշտերի անվանումները հասնեն գերմաներեն թարգմանության մեջ։ Նոր տեղադրումը դրանից չի տուժում։

Մշակողների համար՝ API և hook-եր

Միավորումը կարելի է կանչել նաև սեփական կոդից՝

python

merged = draft_moves.merge_draft_moves(
    extra_vals={"invoice_date": date(2026, 3, 31), "ref": "Q1"},
    source_handling="cancel",   # "delete" կամ "keep"
    add_source_sections=True,   # վերնագրեր. լռելյայն True
)

merge_draft_moves-ը նետում է UserError՝ բոլոր մերժման պատճառներով, եթե ընտրությունը հնարավոր չէ միավորել։ _merge_draft_get_errors()-ը վերադարձնում է նույն պատճառները ցանկի տեսքով՝ առանց exception-ի։

Ով ունի account.move-ի վրա սեփական հետադարձ հղումներ, վերասահմանում է _merge_draft_relink-ը՝

python

class AccountMove(models.Model):
    _inherit = "account.move"

    def _merge_draft_relink(self, new_move):
        super()._merge_draft_relink(new_move)
        if "my_move_id" not in self.env["my.model"]._fields:
            return
        self.env["my.model"].search([("my_move_id", "in", self.ids)]).write(
            {"my_move_id": new_move.id}
        )

Ով ունի հետադարձ հղում հաշվի դիրքի վրա, այս մոդուլը ընդհանրապես պետք չէ. բավական է Odoo-ի սեփական account.move.line._copy_data_extend_business_fields hook-ը, և դաշտը փոխանցվում է միավորելիս, չեղարկելիս և կրկնօրինակելիս հավասարապես։ Այլ Helpdesk մոդուլների կամ վերնագրերի այլ ձևակերպման համար կան _merge_draft_get_source_tickets()-ը և _merge_draft_section_name()-ը։

Փորձարկված է՝ նախքան արտադրական տվյալների վրա աշխատելը

48 թեստ ընդգրկում են խառը կարգավիճակները, տարբեր գործընկերներին, արժույթներին և փաստաթղթի տեսակներին, վաճառքի կամ գնումների պատվերով և առանց դրա դիրքերը, հարկերը, բաժինները և նշումները, ծախսային կենտրոնների բաշխումները, ժամանակի հաշվառման վերակցումը, աղբյուր հաշիվների բոլոր երեք տարբերակները, օգնականը և վերնագրերը՝ որ դրանք լռելյայն ակտիվ են, որ դրանց անջատումը վերարտադրում է հին արդյունքը մինչև ցենտը, որ վերնագիրը գումարներ կամ հաշիվներ չի կրում, որ մեկ աղբյուրի դիրքերը մնում են միասին, որ տիկետը երևում է իր համարով և երբեք տվյալների բազայի ID-ով։

Թեստային տվյալները հիմնվում են ակտիվ ընկերության առկա հաշվային պլանի վրա՝ AccountTestInvoicingCommon-ի փոխարեն. այս helper-ը միգրացված արտադրական տվյալների բազաներում հաճախ չի գործարկվում, իսկ մոդուլը պետք է աշխատի հենց այդպիսիների վրա։

odoo-bin -d <db> -u account_invoice_merge_draft --test-enable \
         --test-tags /account_invoice_merge_draft --stop-after-init

Ինչպես հաշվապահության ոլորտի ցանկացած ընդլայնման դեպքում, մենք խորհուրդ ենք տալիս տեղադրումը նախ ստուգել թեստային միջավայրում՝ սեփական տվյալների բազայի պատճենով։

Անվճար և բաց կոդով՝ Odoo համայնքի համար

Մենք՝ Biricon IT Services GmbH-ում, լուծումներ մշակում ենք նախ և առաջ մեր սեփական գործունեության կոնկրետ պահանջների համար։ Եթե դրանից ստեղծվում է ընդհանուր օգտագործման մոդուլ, մենք չենք ուզում այն թաքցնել մեկ տեղադրման մեջ։

Ուստի Merge Draft Invoices-ը հասանելի է անվճար՝ որպես բաց կոդով նախագիծ։ Սկզբնական կոդը կարելի է դիտել, ստուգել, հարմարեցնել և AGPL-3 լիցենզիայի շրջանակներում զարգացնել։

Մեր նպատակը հնարավորինս կարճ հաշիվը չէր, այլ այնպիսի հաշիվ, որտեղ նույնիսկ մեկ տարի անց յուրաքանչյուր դիրք դեռ տանում է իր պատվերին, իր տիկետին և իր ժամանակի գրանցմանը։

Նախագիծ, սկզբնական կոդ և մասնակցություն

Ամբողջական սկզբնական կոդը, տեղադրման ցուցումները և տեխնիկական փաստաթղթերը հասանելի են GitHub-ում՝

Բացել Odoo 19 Merge Draft Invoices-ը GitHub-ում

Մենք ուրախ կլինենք գործնական փորձի, սխալների մասին հաղորդումների, բարելավման առաջարկների և Odoo համայնքի ներդրումների համար։

Անվճար։ Հետևելի։ Բաց կոդով։ Odoo 19 Community-ի համար։