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