Merge pull request #1785 from bendiy/4_6_x
[xtuple] / foundation-database / public / functions / postcounttaglocation.sql
1 CREATE OR REPLACE FUNCTION postCountTagLocation(INTEGER, BOOLEAN) 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.
4 DECLARE
5   pInvcntid ALIAS FOR $1;
6   pThaw ALIAS FOR $2;
7   _avgCostingMethod TEXT;
8   _invhistid INTEGER;
9   _postDate TIMESTAMP;
10   _runningQty NUMERIC;
11   _errorCode INTEGER;
12   _itemlocSeries INTEGER := 0;
13   _hasDetail BOOLEAN;
14   _p RECORD;
15   _itemloc RECORD;
16   _cntslip RECORD;
17   _origLocQty NUMERIC;
18   _netable BOOLEAN;
19   _lsid INTEGER;
20 BEGIN
21
22   SELECT COALESCE(fetchMetricText('CountAvgCostMethod'), 'STD') INTO _avgCostingMethod;
23
24   SELECT invcnt_id, invcnt_tagnumber, invcnt_qoh_after,
25          invcnt_location_id, invcnt_tagdate,
26          item_number,
27          itemsite_id, itemsite_freeze,
28          itemsite_qtyonhand,
29          itemsite_loccntrl, COALESCE(invcnt_location_id, -1) AS itemsite_location_id,
30          CASE WHEN (itemsite_costmethod = 'N') THEN 0
31               WHEN ( (itemsite_costmethod = 'A') AND
32                      ((itemsite_qtyonhand + itemsite_nnqoh) = 0) AND
33                      (_avgCostingMethod = 'ACT') ) THEN actcost(itemsite_item_id)
34               WHEN ( (itemsite_costmethod = 'A') AND
35                      (_avgCostingMethod IN ('ACT', 'AVG')) ) THEN avgcost(itemsite_id)
36               ELSE stdcost(itemsite_item_id)
37          END AS cost, itemsite_costmethod,
38          itemsite_controlmethod, itemsite_value INTO _p
39   FROM invcnt, itemsite, item
40   WHERE ( (invcnt_itemsite_id=itemsite_id)
41    AND (itemsite_item_id=item_id)
42    AND (invcnt_qoh_after IS NOT NULL)
43    AND (NOT invcnt_posted)
44    AND (invcnt_id=pInvcntid) );
45   IF (NOT FOUND) THEN
46     RETURN -9;
47   END IF;
48
49   SELECT COALESCE(SUM(itemloc_qty),0.0), location_netable INTO _origLocQty,_netable
50     FROM itemloc,location
51    WHERE ((itemloc_itemsite_id=_p.itemsite_id)
52      AND  (location_id=itemloc_location_id)
53      AND  (itemloc_location_id=_p.invcnt_location_id))
54    GROUP BY location_netable;
55   IF (NOT FOUND) THEN
56     _origLocQty := 0.0;
57     _netable := TRUE;
58   END IF;
59
60   SELECT NEXTVAL('invhist_invhist_id_seq') INTO _invhistid;
61
62   IF (_p.itemsite_freeze) THEN
63     _postDate := _p.invcnt_tagdate;
64   ELSE
65     _postDate := CURRENT_TIMESTAMP;
66   END IF;
67
68   _hasDetail = FALSE;
69
70 --  Post the detail indicated by cntslips
71   IF ( (_p.itemsite_loccntrl) OR
72        (_p.itemsite_controlmethod IN ('L', 'S')) ) THEN
73
74     SELECT NEXTVAL('itemloc_series_seq') INTO _itemlocSeries;
75
76 --  Adjust any existing detail to 0
77     FOR _itemloc IN SELECT itemloc_id, itemloc_location_id,
78                            itemloc_ls_id, itemloc_qty
79                     FROM itemloc
80                     WHERE ((itemloc_itemsite_id=_p.itemsite_id)
81                       AND  (itemloc_location_id=_p.invcnt_location_id)) LOOP
82
83       _hasDetail = TRUE;
84
85 --  Create the itemlocdist flushing records
86       INSERT INTO itemlocdist
87       ( itemlocdist_series, itemlocdist_source_type, itemlocdist_source_id,
88         itemlocdist_expiration,
89         itemlocdist_itemsite_id, itemlocdist_invhist_id, itemlocdist_flush )
90       VALUES
91       ( _itemlocSeries, 'I', _itemloc.itemloc_id,
92         endOfTime(),
93         _p.itemsite_id, _invhistid, TRUE );
94
95     END LOOP;
96
97 --  Clear the running detail Qty
98     _runningQty := 0;
99
100 --  Adjust the detail to the cntslip indicated value
101     FOR _cntslip IN SELECT cntslip_location_id, cntslip_lotserial,
102                            cntslip_lotserial_expiration,
103                            cntslip_lotserial_warrpurc,
104                            SUM(cntslip_qty) AS qty,
105                            itemsite_item_id
106                     FROM cntslip,invcnt,itemsite
107                     WHERE ((cntslip_cnttag_id=pInvcntid)
108                     AND (cntslip_cnttag_id=invcnt_id)
109                     AND (invcnt_itemsite_id=itemsite_id))
110                     GROUP BY cntslip_location_id, cntslip_lotserial, cntslip_lotserial_expiration,
111                     cntslip_lotserial_warrpurc, itemsite_item_id LOOP
112
113 --  Handle the LotSerial
114       IF (LENGTH(_cntslip.cntslip_lotserial)>0) THEN
115         SELECT ls_id INTO _lsid
116         FROM ls
117         WHERE ((ls_item_id=_cntslip.itemsite_item_id)
118         AND (UPPER(ls_number)=UPPER(_cntslip.cntslip_lotserial)));
119
120         IF (NOT FOUND) THEN
121           _lsid := NEXTVAL('ls_ls_id_seq');
122           INSERT INTO ls
123           VALUES (_lsid,_cntslip.itemsite_item_id,UPPER(_cntslip.cntslip_lotserial));
124         END IF;
125       END IF;
126        
127 --  Track the running Qty
128       _runningQty := (_runningQty + _cntslip.qty);
129       _hasDetail = TRUE;
130
131 --  Create the itemlocdist populating record
132       INSERT INTO itemlocdist
133       ( itemlocdist_series, itemlocdist_source_type, itemlocdist_source_id,
134         itemlocdist_itemsite_id,
135         itemlocdist_ls_id, itemlocdist_expiration, itemlocdist_warranty,
136         itemlocdist_qty, itemlocdist_invhist_id )
137       VALUES
138       ( _itemlocSeries, 'L', _cntslip.cntslip_location_id,
139         _p.itemsite_id,
140         _lsid, COALESCE(_cntslip.cntslip_lotserial_expiration, endOfTime()),
141         _cntslip.cntslip_lotserial_warrpurc,
142         _cntslip.qty, _invhistid );
143
144     END LOOP;
145
146     IF (_runningQty > _p.invcnt_qoh_after) THEN
147 --  The total Count Slip Qty is greater than the Count Tag Qty,
148 --  Don't post the Count.
149       _errorCode = -1;
150
151     ELSIF ( (_runningQty < _p.invcnt_qoh_after) AND
152             (_p.itemsite_controlmethod IN ('L', 'S')) ) THEN
153 --  The total Count Slip Qty is less than the Count Tag Qty,
154 --  and the Item Site is Lot/Serial controlled.
155 --  Don't post the Count.
156       _errorCode = -2;
157
158     ELSIF (_runningQty < _p.invcnt_qoh_after) THEN
159       IF ( (NOT _p.itemsite_loccntrl) OR
160            (_p.itemsite_location_id = -1) ) THEN
161 --  The total Count Slip Qty is less than the Count Tag Qty,
162 --  and there isn't a default location to post into.
163 --  Don't post the Count.
164         _errorCode = -3;
165
166       ELSIF ( SELECT (metric_value='f')
167               FROM metric
168               WHERE (metric_name='PostCountTagToDefault') ) THEN
169 --  The total Count Slip Qty is less than the Count Tag Qty,
170 --  and we don't post Count Tags to default Locations
171 --  Don't post the Count.
172         _errorCode = -4;
173
174       ELSE
175 --  Distribute the remaining qty into the default location.
176         INSERT INTO itemlocdist
177         ( itemlocdist_series, itemlocdist_source_type, itemlocdist_source_id,
178           itemlocdist_itemsite_id,
179           itemlocdist_ls_id, itemlocdist_expiration,
180           itemlocdist_qty, itemlocdist_invhist_id )
181         SELECT _itemlocSeries, 'L', _p.itemsite_location_id,
182                _p.itemsite_id,
183                _lsid, endOfTime(),
184                (_p.invcnt_qoh_after - _runningQty), _invhistid;
185
186         _hasDetail = TRUE;
187         _errorCode = 0;
188       END IF;
189     ELSE
190 --  The Count Slip Qty. must equal the Count Tag Qty.
191       _errorCode = 0;
192     END IF;
193
194 --  If we shouldn't post the count then delete the itemlocdist records,
195 --  and return with the error.
196     IF (_errorCode <> 0) THEN
197       DELETE FROM itemlocdist
198       WHERE (itemlocdist_series=_itemlocSeries);
199   
200       RETURN _errorCode;
201     END IF;
202
203   END IF;
204
205 --  Mod. the Count Tag.
206   UPDATE invcnt
207   SET invcnt_qoh_before=_origLocQty,
208       invcnt_postdate=_postDate,
209       invcnt_posted=TRUE,
210       invcnt_invhist_id=_invhistid,
211       invcnt_post_username=getEffectiveXtUser()
212   WHERE (invcnt_id=pInvcntid);
213
214 --  Create the CC transaction
215   INSERT INTO invhist
216    ( invhist_id, invhist_itemsite_id,
217      invhist_transdate, invhist_transtype, invhist_invqty,
218      invhist_qoh_before, invhist_qoh_after,
219      invhist_docnumber, invhist_comments,
220      invhist_invuom, invhist_unitcost, invhist_hasdetail,
221      invhist_costmethod, invhist_value_before, invhist_value_after,
222      invhist_series )
223   SELECT _invhistid, itemsite_id,
224          _postDate, 'CC', (invcnt_qoh_after - invcnt_qoh_before),
225          invcnt_qoh_before, invcnt_qoh_after,
226          invcnt_tagnumber, invcnt_comments,
227          uom_name, _p.cost, _hasDetail,
228          _p.itemsite_costmethod, _p.itemsite_value,
229          _p.itemsite_value + (_p.cost * (invcnt_qoh_after - invcnt_qoh_before)),
230          _itemlocSeries
231   FROM itemsite, invcnt, item, uom
232   WHERE ( (invcnt_itemsite_id=itemsite_id)
233    AND (itemsite_item_id=item_id)
234    AND (item_inv_uom_id=uom_id)
235    AND (itemsite_controlmethod <> 'N')
236    AND (invcnt_id=pInvcntid) );
237
238 --  Update the QOH
239   IF (_netable) THEN
240     UPDATE itemsite
241     SET itemsite_qtyonhand= itemsite_qtyonhand + (_p.invcnt_qoh_after - _origLocQty),
242         itemsite_datelastcount=_postDate
243     WHERE (itemsite_id=_p.itemsite_id);
244     UPDATE itemsite
245     SET itemsite_value =  (itemsite_qtyonhand + itemsite_nnqoh) * _p.cost
246     WHERE (itemsite_id=_p.itemsite_id);
247   ELSE
248     UPDATE itemsite
249     SET itemsite_nnqoh =  itemsite_nnqoh + (_p.invcnt_qoh_after - _origLocQty),
250         itemsite_datelastcount=_postDate
251     WHERE (itemsite_id=_p.itemsite_id);
252     UPDATE itemsite
253     SET itemsite_value =  (itemsite_qtyonhand + itemsite_nnqoh) * _p.cost
254     WHERE (itemsite_id=_p.itemsite_id);
255   END IF;
256  
257 --  Post the detail, if any
258   IF (_hasDetail) THEN
259     PERFORM distributeItemlocSeries(_itemlocSeries);
260   END IF;
261
262 --  Thaw the itemsite if it's frozen
263   IF (pThaw) THEN
264     PERFORM thawItemSite(invcnt_itemsite_id) 
265     FROM invcnt
266     WHERE (invcnt_id=pInvcntid);
267   END IF;
268
269 --  Distribute to G/L
270   PERFORM insertGLTransaction( 'I/M', 'CT', _p.invcnt_tagnumber, ('Post Count Tag #' || _p.invcnt_tagnumber || ' for Item ' || _p.item_number),
271                                costcat_adjustment_accnt_id, costcat_asset_accnt_id, _invhistid,
272                                ( (_p.invcnt_qoh_after - _origLocQty) * _p.cost), CURRENT_DATE )
273   FROM invcnt, itemsite, costcat
274   WHERE ( (invcnt_itemsite_id=itemsite_id)
275    AND (itemsite_costcat_id=costcat_id)
276    AND (invcnt_id=pInvcntid) );
277
278   RETURN 0;
279 END;
280 $$ LANGUAGE 'plpgsql';