1 CREATE OR REPLACE FUNCTION distributeToLocations(pItemlocdistid INTEGER) RETURNS INTEGER AS $$
2 -- Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
3 -- See www.xtuple.com/CPAL for the full text of the software license.
16 -- A scenario can occur where two people try to post distributions
17 -- to the same itemsite against two or more lot/serial/mlc locations
18 -- leading to a deadlock. This line tries to prevent that by locking
19 -- ahead of time all the itemsites that the transaction will need
20 -- before any of the other tables are locked individually.
24 WHERE(itemsite_id in (SELECT DISTINCT itemlocdist_itemsite_id
26 WHERE(itemlocdist_id=pItemlocdistid)))
29 -- March through all of the itemlocdist owned by the passed parent itemlocdist
30 FOR _itemlocdist IN SELECT c.itemlocdist_id AS itemlocdistid,
31 c.itemlocdist_source_type AS type,
32 c.itemlocdist_source_id AS sourceid,
33 c.itemlocdist_qty AS qty,
34 p.itemlocdist_itemsite_id AS itemsiteid,
36 p.itemlocdist_invhist_id AS invhistid,
37 p.itemlocdist_ls_id AS lotserialid,
38 p.itemlocdist_expiration AS expiration,
39 p.itemlocdist_warranty AS warranty,
40 p.itemlocdist_order_type AS ordertype,
41 p.itemlocdist_order_id AS orderid,
42 p.itemlocdist_series AS series
43 FROM itemlocdist AS c, itemlocdist AS p, itemsite
44 WHERE ( (c.itemlocdist_itemlocdist_id=p.itemlocdist_id)
45 AND (p.itemlocdist_source_type='O')
46 AND (p.itemlocdist_itemsite_id=itemsite_id)
47 AND (p.itemlocdist_id=pItemlocdistid) ) LOOP
49 _distCounter := _distCounter + 1;
51 -- If the target for this itemlocdist is a location, check to see if the
52 -- required itemloc already exists
53 IF (_itemlocdist.type = 'L') THEN
54 SELECT itemloc_id INTO _itemlocid
56 WHERE ( (itemloc_itemsite_id=_itemlocdist.itemsiteid)
57 AND (itemloc_location_id=_itemlocdist.sourceid)
58 AND (COALESCE(itemloc_ls_id, -1)=COALESCE(_itemlocdist.lotserialid, -1))
59 AND (COALESCE(itemloc_expiration,endOfTime())=COALESCE(_itemlocdist.expiration,endOfTime()))
60 AND (COALESCE(itemloc_warrpurc,endoftime())=COALESCE(_itemlocdist.warranty,endoftime())) );
64 SELECT NEXTVAL('itemloc_itemloc_id_seq') INTO _itemlocid;
66 ( itemloc_id, itemloc_itemsite_id,
67 itemloc_location_id, itemloc_qty,
68 itemloc_ls_id, itemloc_expiration,
71 ( _itemlocid, _itemlocdist.itemsiteid,
72 _itemlocdist.sourceid, 0,
73 _itemlocdist.lotserialid, _itemlocdist.expiration,
74 _itemlocdist.warranty );
79 _itemlocid = _itemlocdist.sourceid;
81 IF (_itemlocid IS NOT NULL AND (SELECT count(itemloc_id) = 0 FROM itemloc WHERE itemloc_id=_itemlocid)) THEN
82 RAISE EXCEPTION 'No record to distribute against. Someone else may have already distributed this record.';
86 -- Record the invdetail for this itemlocdist
88 ( invdetail_invhist_id, invdetail_location_id, invdetail_ls_id,
89 invdetail_qty, invdetail_qty_before, invdetail_qty_after, invdetail_expiration,
91 SELECT _itemlocdist.invhistid, itemloc_location_id, itemloc_ls_id,
92 _itemlocdist.qty, itemloc_qty, (itemloc_qty + _itemlocdist.qty),
93 itemloc_expiration,_itemlocdist.warranty
95 WHERE (itemloc_id=_itemlocid);
97 -- Update the parent invhist to indicate that it has invdetail records
99 SET invhist_hasdetail=TRUE
100 WHERE ((invhist_hasdetail=FALSE)
101 AND (invhist_id=_itemlocdist.invhistid));
103 -- Update the itemloc_qty if its parent itemsite is not frozen
104 IF (NOT _itemlocdist.itemsite_freeze) THEN
106 SET itemloc_qty = (itemloc_qty + _itemlocdist.qty)
107 WHERE (itemloc_id=_itemlocid);
109 PERFORM postInvHist(_itemlocdist.invhistid);
111 -- Handle reservation data
112 IF ( (SELECT fetchMetricBool('EnableSOReservationsByLocation')) AND
113 (_itemlocdist.qty < 0) ) THEN
115 -- If a shipment on a sales order, record reservation change before updating
116 -- so it can be reversed later if necessary
117 IF (_itemlocdist.ordertype = 'SO') THEN
118 INSERT INTO shipitemlocrsrv
119 SELECT nextval('shipitemlocrsrv_shipitemlocrsrv_id_seq'),
120 shipitem_id, itemloc_itemsite_id, itemloc_location_id,
121 itemloc_ls_id, itemloc_expiration, itemloc_warrpurc,
122 least((_itemlocdist.qty * -1.0), reserve_qty)
123 FROM shipitem, itemloc
124 JOIN reserve ON (itemloc_id=reserve_supply_id AND reserve_supply_type='I')
125 WHERE ( (shipitem_invhist_id=_itemlocdist.invhistid)
126 AND (itemloc_id=_itemlocid)
127 AND (reserve_demand_type=_itemlocdist.ordertype)
128 AND (reserve_demand_id=_itemlocdist.orderid) );
131 -- Update the reservation
133 SET reserve_qty = (reserve_qty + _itemlocdist.qty)
134 WHERE ( (reserve_supply_id=_itemlocid)
135 AND (reserve_supply_type='I')
136 AND (reserve_demand_type=_itemlocdist.ordertype)
137 AND (reserve_demand_id=_itemlocdist.orderid) );
139 -- Delete reservation if fully distributed
141 WHERE ( (reserve_supply_id=_itemlocid)
142 AND (reserve_supply_type='I')
143 AND (reserve_demand_type=_itemlocdist.ordertype)
144 AND (reserve_demand_id=_itemlocdist.orderid)
145 AND (reserve_qty=0) );
149 -- Cache the running qty.
150 _runningQty := _runningQty + _itemlocdist.qty;
152 -- Dene with the child itemlocdist, so delete it
153 DELETE FROM itemlocdist
154 WHERE (itemlocdist_id=_itemlocdist.itemlocdistid);
156 -- If the target itemloc is now at qty=0, delete it if its parent
157 -- itemsite is not frozen
158 IF (NOT _itemlocdist.itemsite_freeze) THEN
160 WHERE ( (itemloc_qty=0)
161 AND (itemloc_id=_itemlocid) );
166 -- If the running qty for the detailed distributions is the same as the
167 -- total qty to distribute indicated by the parent itemlocdist, then the
168 -- parent itemlocdist has been fully distributed and should be deleted.
169 IF ( ( SELECT itemlocdist_qty
171 WHERE (itemlocdist_id=pItemlocdistid) ) = _runningQty) THEN
172 DELETE FROM itemlocdist
173 WHERE (itemlocdist_id=pItemlocdistid);
175 -- There is still some more qty to distribute in the parent itemlocdist.
176 -- Update the qty to distribute with the qty that has been distributed.
178 SET itemlocdist_qty = (itemlocdist_qty - _runningQty)
179 WHERE (itemlocdist_id=pItemlocdistid);