[ 
https://issues.apache.org/jira/browse/OFBIZ-2456?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12710138#action_12710138
 ] 

Karim Rahimpur commented on OFBIZ-2456:
---------------------------------------

I really agree with you on the first option. Actually we were missing part of 
this in a current project, to be more specific: sales invoices use enforced 
sequence without prefix and thus purchase invoices using the same sequence 
numbering (also without prefix) is not workable. So I'll outline a solution to 
that in the hope it will help you or anyone who needs this. It's actually quite 
simple for invoices at least (I don't care much about purchase orders etc. at 
least in this case):

_The idea: Distinguish between invoice numbers for sales and purchase invoices._


*1. Add attributes to PartyAcctgPreference in 
accounting/entitydef/entitymodel.xml*

{code}    <entity entity-name="PartyAcctgPreference"
        package-name="org.ofbiz.accounting.ledger"
        title="Party (organization) accounting preferences">
        ...
        <field name="purchaseInvoiceSequenceEnumId" type="id-ne"/>
        <field name="purchaseInvoiceIdPrefix" type="very-short"/>
        <field name="lastPurchaseInvoiceNumber" type="numeric"/>
        <field name="lastPurchaseInvoiceRestartDate" type="date-time"/>
        <field name="usePurchaseInvoiceIdForReturns" type="indicator"/>
        ...
        <relation type="one" fk-name="ACTGPREF_PINVSQ" 
title="PurchaseInvoiceSequence" rel-entity-name="Enumeration">
            <key-map field-name="purchaseInvoiceSequenceEnumId" 
rel-field-name="enumId"/>
        </relation>
        ...
    </entity>
{code}

*Provide the invoice type to getNextInvoiceId service/method*

  In accounting/servicedef/services_invoice.xml add the invoiceTypeId and the 
billed party (partyIdTo) as an optional input parameter:

{code}    <service name="getNextInvoiceId" engine="simple"
        location="org/ofbiz/accounting/invoice/InvoiceServices.xml" 
invoke="getNextInvoiceId">
        <description>Get the Next Invoice ID According to Settings on the 
PartyAcctgPreference Entity for the given Party</description>
        <attribute name="partyId" type="String" mode="IN" optional="false"/>
        <attribute name="partyIdTo" type="String" mode="IN" optional="true"/>
        <attribute name="invoiceTypeId" type="String" mode="IN" 
optional="true"/>
        <attribute name="invoiceId" type="String" mode="OUT" optional="false"/>
    </service>
{code}

*In accounting/script/.../InvoiceServices.xml for the simple method 
createInvoice pass the additional parameters:*

{code}    <simple-method method-name="createInvoice" short-description="Create 
a new Invoice">
        <now-timestamp field="nowTimestamp"/>

        <make-value value-field="newEntity" entity-name="Invoice"/>
        <set-nonpk-fields map="parameters" value-field="newEntity"/>

        <!-- call getNextInvoiceId service with the parameters.partyIdFrom when 
invoice Id is not suplied
                            else use it from the input -->
        <if-empty field="parameters.invoiceId">
            <set field="getNextInvoiceIdMap.partyId" 
from-field="parameters.partyIdFrom"/>
            <set field="getNextInvoiceIdMap.partyIdTo" 
from-field="parameters.partyId"/>
            <set field="getNextInvoiceIdMap.invoiceTypeId" 
from-field="parameters.invoiceTypeId"/>
            <call-service service-name="getNextInvoiceId" 
in-map-name="getNextInvoiceIdMap">
                <result-to-field result-name="invoiceId" 
field="newEntity.invoiceId"/>
            </call-service>
            ...
    </simple-method>
{code}


*Adapt the getNextInvoiceId method*

Just the same code as before but distinguishes between sales and purchase 
invoices
to get the prefix and sequence number.


{code}    <simple-method method-name="getNextInvoiceId" short-description="Get 
Next invoiceId">
        <!-- try to find PartyAcctgPreference for parameters.partyId, see if we 
need any special invoice number sequencing -->
        <entity-one entity-name="PartyAcctgPreference" 
value-field="partyAcctgPreference" auto-field-map="false">
            <field-map field-name="partyId" from-field="parameters.partyId"/>
        </entity-one>
        <entity-one entity-name="InvoiceType" value-field="invoiceType">
            <field-map field-name="invoiceTypeId" 
value="${parameters.invoiceTypeId}"/>
        </entity-one>
        </entity-one>
        <log level="info" message="In getNextInvoiceId partyId is 
[${parameters.partyId}], partyAcctgPreference: ${partyAcctgPreference}, 
invoiceType: ${invoiceType}"/>

        <if>
            <condition>
                <or>
                    <if-empty field="invoiceType"/>
                    <if-compare field="invoiceType.parentTypeId" 
operator="equals" value="SALES_INVOICE"/>
                </or>
            </condition>
            <then>
                <!-- get a sequence number for a sales invoice -->
                <if>
                    <condition>
                        <if-compare 
field="partyAcctgPreference.invoiceSequenceEnumId" operator="equals" 
value="INVSQ_ENF_SEQ"/>
                    </condition>
                    <then>
                        <log level="info" message="In createInvoice sequence 
enum INVSQ_ENF_SEQ"/>
                        <!-- this is sequential sequencing, we can't skip a 
number, also it must be a unique sequence per partyIdFrom -->
                        <if-not-empty 
field="partyAcctgPreference.lastInvoiceNumber">
                            <calculate 
field="partyAcctgPreference.lastInvoiceNumber" type="Long">
                                <calcop operator="add" 
field="partyAcctgPreference.lastInvoiceNumber"/>
                                <number value="1"/>
                            </calculate>
                            <else>
                                <calculate 
field="partyAcctgPreference.lastInvoiceNumber" type="Long"><number 
value="1"/></calculate>
                            </else>
                        </if-not-empty>
                        <store-value value-field="partyAcctgPreference"/>
                        <set 
from-field="partyAcctgPreference.lastInvoiceNumber" field="invoiceIdTemp"/>
                    </then>
                    <else-if>
                        <condition>
                            <if-compare 
field="partyAcctgPreference.invoiceSequenceEnumId" operator="equals" 
value="INVSQ_RESTARTYR"/>
                        </condition>
                        <then>
                            <log level="info" message="In createInvoice 
sequence enum INVSQ_RESTARTYR"/>
                            <!-- this is sequential sequencing, we can't skip a 
number; except that it is restarted each fiscal year -->

                            <now-timestamp field="nowTimestamp"/>
                            <if-empty 
field="partyAcctgPreference.lastInvoiceRestartDate">
                                <!-- if no lastInvoiceRestartDate then it's 
easy, just start now with 1 -->
                                <calculate 
field="partyAcctgPreference.lastInvoiceNumber" type="Long"><number 
value="1"/></calculate>
                                <set from-field="nowTimestamp" 
field="partyAcctgPreference.lastInvoiceRestartDate"/>
                                <else>
                                    <!-- first figure out if we need to reset 
the lastInvoiceNumber; is the lastInvoiceRestartDate after the 
fiscalYearStartMonth/Day for this year? -->
                                    <calculate field="zeroLong" 
type="Long"><number value="0"/></calculate>
                                    <call-class-method 
class-name="org.ofbiz.base.util.UtilDateTime" method-name="getYearStart" 
ret-field="curYearFiscalStartDate">
                                        <field field="nowTimestamp" 
type="java.sql.Timestamp"/>
                                        <field 
field="partyAcctgPreference.fiscalYearStartDay" type="java.lang.Number"/>
                                        <field 
field="partyAcctgPreference.fiscalYearStartMonth" type="java.lang.Number"/>
                                        <field field="zeroLong" 
type="java.lang.Number"/>
                                    </call-class-method>
                                    <if>
                                        <condition>
                                            <and>
                                                <if-compare-field 
field="partyAcctgPreference.lastInvoiceRestartDate" 
to-field="curYearFiscalStartDate" operator="less" type="Timestamp"/>
                                                <if-compare-field 
field="nowTimestamp" to-field="curYearFiscalStartDate" 
operator="greater-equals" type="Timestamp"/>
                                            </and>
                                        </condition>
                                        <then>
                                            <!-- less than fiscal year start, 
we need to reset it -->
                                            <calculate 
field="partyAcctgPreference.lastInvoiceNumber" type="Long"><number 
value="1"/></calculate>
                                            <set from-field="nowTimestamp" 
field="partyAcctgPreference.lastInvoiceRestartDate"/>
                                        </then>
                                        <else>
                                            <!-- greater than or equal to 
fiscal year start or nowTimestamp hasn't yet hit the current year fiscal start 
date, we're okay, just increment -->
                                            <calculate 
field="partyAcctgPreference.lastInvoiceNumber" type="Long">
                                                <calcop operator="add" 
field="partyAcctgPreference.lastInvoiceNumber"/>
                                                <number value="1"/>
                                            </calculate>
                                        </else>
                                    </if>
                                </else>
                            </if-empty>
                            <store-value value-field="partyAcctgPreference"/>

                            <!-- get the current year string for prefix, etc; 
simple 4 digit year date string (using system defaults) -->
                            <set field="curYearString" 
value="${str:toString(date:year(partyAcctgPreference.lastInvoiceRestartDate, 
util:defaultTimeZone(), util:defaultLocale()))}"/>
                            <set field="invoiceIdTemp" 
value="${curYearString}-${str:toString(partyAcctgPreference.lastInvoiceNumber)}"/>
                        </then>
                    </else-if>
                    <else>
                        <log level="info" message="In createInvoice sequence 
enum INVSQ_STANDARD"/>
                        <!-- default to the default sequencing: INVSQ_STANDARD 
-->
                        <set from-field="parameters.invoiceId" 
field="invoiceIdTemp"/>
                        <if-empty field="invoiceIdTemp">
                            <sequenced-id sequence-name="Invoice" 
field="invoiceIdTemp"/>
                            <else>
                                <!-- check the provided ID -->
                                <check-id field="invoiceIdTemp"/>
                                <check-errors/>
                            </else>
                        </if-empty>
                    </else>
                </if>

                <!-- use invoiceIdTemp along with the invoiceIdPrefix to create 
the real ID -->
                <set field="invoiceId" 
value="${partyAcctgPreference.invoiceIdPrefix}${str:toString(invoiceIdTemp)}"/>
                <field-to-result field="invoiceId" result-name="invoiceId"/>
            </then>
            <else>
                <!-- get a sequence number for a purchase invoice -->
                <if>
                    <condition>
                        <if-compare 
field="partyAcctgPreference.purchaseInvoiceSequenceEnumId" operator="equals" 
value="INVSQ_ENF_SEQ"/>
                    </condition>
                    <then>
                        <log level="info" message="In createInvoice sequence 
enum INVSQ_ENF_SEQ"/>
                        <!-- this is sequential sequencing, we can't skip a 
number, also it must be a unique sequence per partyIdFrom -->
                        <if-not-empty 
field="partyAcctgPreference.lastPurchaseInvoiceNumber">
                            <calculate 
field="partyAcctgPreference.lastPurchaseInvoiceNumber" type="Long">
                                <calcop operator="add" 
field="partyAcctgPreference.lastPurchaseInvoiceNumber"/>
                                <number value="1"/>
                            </calculate>
                            <else>
                                <calculate 
field="partyAcctgPreference.lastPurchaseInvoiceNumber" type="Long"><number 
value="1"/></calculate>
                            </else>
                        </if-not-empty>
                        <store-value value-field="partyAcctgPreference"/>
                        <set 
from-field="partyAcctgPreference.lastPurchaseInvoiceNumber" 
field="invoiceIdTemp"/>
                    </then>
                    <else-if>
                        <condition>
                            <if-compare 
field="partyAcctgPreference.purchaseInvoiceSequenceEnumId" operator="equals" 
value="INVSQ_RESTARTYR"/>
                        </condition>
                        <then>
                            <log level="info" message="In createInvoice 
sequence enum INVSQ_RESTARTYR"/>
                            <!-- this is sequential sequencing, we can't skip a 
number; except that it is restarted each fiscal year -->

                            <now-timestamp field="nowTimestamp"/>
                            <if-empty 
field="partyAcctgPreference.lastPurchaseInvoiceRestartDate">
                                <!-- if no lastInvoiceRestartDate then it's 
easy, just start now with 1 -->
                                <calculate 
field="partyAcctgPreference.lastPurchaseInvoiceNumber" type="Long"><number 
value="1"/></calculate>
                                <set from-field="nowTimestamp" 
field="partyAcctgPreference.lastPurchaseInvoiceRestartDate"/>
                                <else>
                                    <!-- first figure out if we need to reset 
the lastPurchaseInvoiceNumber; is the lastPurchaseInvoiceRestartDate after the 
fiscalYearStartMonth/Day for this year? -->
                                    <calculate field="zeroLong" 
type="Long"><number value="0"/></calculate>
                                    <call-class-method 
class-name="org.ofbiz.base.util.UtilDateTime" method-name="getYearStart" 
ret-field="curYearFiscalStartDate">
                                        <field field="nowTimestamp" 
type="java.sql.Timestamp"/>
                                        <field 
field="partyAcctgPreference.fiscalYearStartDay" type="java.lang.Number"/>
                                        <field 
field="partyAcctgPreference.fiscalYearStartMonth" type="java.lang.Number"/>
                                        <field field="zeroLong" 
type="java.lang.Number"/>
                                    </call-class-method>
                                    <if>
                                        <condition>
                                            <and>
                                                <if-compare-field 
field="partyAcctgPreference.lastPurchaseInvoiceRestartDate" 
to-field="curYearFiscalStartDate" operator="less" type="Timestamp"/>
                                                <if-compare-field 
field="nowTimestamp" to-field="curYearFiscalStartDate" 
operator="greater-equals" type="Timestamp"/>
                                            </and>
                                        </condition>
                                        <then>
                                            <!-- less than fiscal year start, 
we need to reset it -->
                                            <calculate 
field="partyAcctgPreference.lastPurchaseInvoiceNumber" type="Long"><number 
value="1"/></calculate>
                                            <set from-field="nowTimestamp" 
field="partyAcctgPreference.lastPurchaseInvoiceRestartDate"/>
                                        </then>
                                        <else>
                                            <!-- greater than or equal to 
fiscal year start or nowTimestamp hasn't yet hit the current year fiscal start 
date, we're okay, just increment -->
                                            <calculate 
field="partyAcctgPreference.lastPurchaseInvoiceNumber" type="Long">
                                                <calcop operator="add" 
field="partyAcctgPreference.lastPurchaseInvoiceNumber"/>
                                                <number value="1"/>
                                            </calculate>
                                        </else>
                                    </if>
                                </else>
                            </if-empty>
                            <store-value value-field="partyAcctgPreference"/>

                            <!-- get the current year string for prefix, etc; 
simple 4 digit year date string (using system defaults) -->
                            <set field="curYearString" 
value="${str:toString(date:year(partyAcctgPreference.lastPurchaseInvoiceRestartDate,
 util:defaultTimeZone(), util:defaultLocale()))}"/>
                            <set field="invoiceIdTemp" 
value="${curYearString}-${str:toString(partyAcctgPreference.lastPurchaseInvoiceNumber)}"/>
                        </then>
                    </else-if>
                    <else>
                        <log level="info" message="In createInvoice sequence 
enum INVSQ_STANDARD"/>
                        <!-- default to the default sequencing: INVSQ_STANDARD 
-->
                        <set from-field="parameters.invoiceId" 
field="invoiceIdTemp"/>
                        <if-empty field="invoiceIdTemp">
                            <sequenced-id sequence-name="Invoice" 
field="invoiceIdTemp"/>
                            <else>
                                <!-- check the provided ID -->
                                <check-id field="invoiceIdTemp"/>
                                <check-errors/>
                            </else>
                        </if-empty>
                    </else>
                </if>
                
                <!-- use invoiceIdTemp along with the invoiceIdPrefix to create 
the real ID -->
                <set field="invoiceId" 
value="${partyAcctgPreference.purchaseInvoiceIdPrefix}${str:toString(invoiceIdTemp)}"/>
                <field-to-result field="invoiceId" result-name="invoiceId"/>
            </else>
        </if>
    </simple-method>
{code}

Sorry for not providing a patch but the version we're working on is not in sync 
with the current revision, but I think there should not be much difference. 
Hope this helps.

> Allow to control Prefix and Purchase Order sequence.
> ----------------------------------------------------
>
>                 Key: OFBIZ-2456
>                 URL: https://issues.apache.org/jira/browse/OFBIZ-2456
>             Project: OFBiz
>          Issue Type: New Feature
>          Components: accounting
>    Affects Versions: SVN trunk
>            Reporter: Jacques Le Roux
>             Fix For: SVN trunk
>
>
> For the moment the Data Model does not take into account the possibility to 
> control Prefix and Purchase Order sequence.
> In PartyAcctgPreference, there are the couples of fields 
> (invoiceSequenceEnumId, invoiceIdPrefix), (quoteSequenceEnumId, 
> quoteIdPrefix) and (orderSequenceEnumId, orderIdPrefix). So we should either 
> add a couple (purchInvoiceSequenceEnumId, purchinvoiceIdPrefix), which IMO is 
> simpler and enough, or deprecate the couple (invoiceSequenceEnumId, 
> invoiceIdPrefix) to (salesInvoiceSequenceEnumId, salesInvoiceIdPrefix) and 
> add the couple above. Then the same User Interface than for Sales Order  
> (configuration settings in Organization GL Settings) could be used but it 
> would have to differentiate the 2 kinds of invoices.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to