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