123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090 |
- # -*- coding: utf-8 -*-
- # Part of Odoo. See LICENSE file for full copyright and licensing details.
- from odoo import Command
- from odoo.exceptions import UserError
- from odoo.tests import Form
- from odoo.tests.common import TransactionCase
- class StockMove(TransactionCase):
- @classmethod
- def setUpClass(cls):
- super(StockMove, cls).setUpClass()
- group_stock_multi_locations = cls.env.ref('stock.group_stock_multi_locations')
- cls.env.user.write({'groups_id': [(4, group_stock_multi_locations.id, 0)]})
- cls.stock_location = cls.env.ref('stock.stock_location_stock')
- cls.customer_location = cls.env.ref('stock.stock_location_customers')
- cls.supplier_location = cls.env.ref('stock.stock_location_suppliers')
- cls.pack_location = cls.env.ref('stock.location_pack_zone')
- cls.pack_location.active = True
- cls.transit_location = cls.env['stock.location'].search([
- ('company_id', '=', cls.env.company.id),
- ('usage', '=', 'transit'),
- ('active', '=', False)
- ], limit=1)
- cls.transit_location.active = True
- cls.uom_unit = cls.env.ref('uom.product_uom_unit')
- cls.uom_dozen = cls.env.ref('uom.product_uom_dozen')
- cls.product = cls.env['product.product'].create({
- 'name': 'Product A',
- 'type': 'product',
- 'categ_id': cls.env.ref('product.product_category_all').id,
- })
- cls.product_serial = cls.env['product.product'].create({
- 'name': 'Product A',
- 'type': 'product',
- 'tracking': 'serial',
- 'categ_id': cls.env.ref('product.product_category_all').id,
- })
- cls.product_lot = cls.env['product.product'].create({
- 'name': 'Product A',
- 'type': 'product',
- 'tracking': 'lot',
- 'categ_id': cls.env.ref('product.product_category_all').id,
- })
- cls.product_consu = cls.env['product.product'].create({
- 'name': 'Product A',
- 'type': 'consu',
- 'categ_id': cls.env.ref('product.product_category_all').id,
- })
- def gather_relevant(self, product_id, location_id, lot_id=None, package_id=None, owner_id=None, strict=False):
- quants = self.env['stock.quant']._gather(product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=strict)
- return quants.filtered(lambda q: not (q.quantity == 0 and q.reserved_quantity == 0))
- def test_in_1(self):
- """ Receive products from a supplier. Check that a move line is created and that the
- reception correctly increase a single quant in stock.
- """
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # fill the move line
- move_line = move1.move_line_ids[0]
- self.assertEqual(move_line.reserved_qty, 100.0)
- self.assertEqual(move_line.qty_done, 0.0)
- move_line.qty_done = 100.0
- # validation
- move1._action_done()
- self.assertEqual(move1.state, 'done')
- # no quants are created in the supplier location
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.supplier_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.supplier_location, allow_negative=True), -100.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 100.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.supplier_location)), 1.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- def test_in_2(self):
- """ Receive 5 tracked products from a supplier. The create move line should have 5
- reserved. If i assign the 5 items to lot1, the reservation should not change. Once
- i validate, the reception correctly increase a single quant in stock.
- """
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- move_line = move1.move_line_ids[0]
- self.assertEqual(move_line.reserved_qty, 5)
- move_line.lot_name = 'lot1'
- move_line.qty_done = 5.0
- self.assertEqual(move_line.reserved_qty, 5) # don't change reservation
- move1._action_done()
- self.assertEqual(move_line.reserved_qty, 0) # change reservation to 0 for done move
- self.assertEqual(move1.state, 'done')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.supplier_location), 0.0)
- supplier_quants = self.gather_relevant(self.product_lot, self.supplier_location)
- self.assertEqual(sum(supplier_quants.mapped('quantity')), -5.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location), 5.0)
- self.assertEqual(len(self.gather_relevant(self.product_lot, self.supplier_location)), 1.0)
- quants = self.gather_relevant(self.product_lot, self.stock_location)
- self.assertEqual(len(quants), 1.0)
- for quant in quants:
- self.assertNotEqual(quant.in_date, False)
- def test_in_3(self):
- """ Receive 5 serial-tracked products from a supplier. The system should create 5 different
- move line.
- """
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 5)
- move_line = move1.move_line_ids[0]
- self.assertEqual(move1.reserved_availability, 5)
- i = 0
- for move_line in move1.move_line_ids:
- move_line.lot_name = 'sn%s' % i
- move_line.qty_done = 1
- i += 1
- self.assertEqual(move1.quantity_done, 5.0)
- self.assertEqual(move1.product_qty, 5) # don't change reservation
- move1._action_done()
- self.assertEqual(move1.quantity_done, 5.0)
- self.assertEqual(move1.product_qty, 5) # don't change reservation
- self.assertEqual(move1.state, 'done')
- # Quant balance should result with 5 quant in supplier and stock
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.supplier_location), 0.0)
- supplier_quants = self.gather_relevant(self.product_serial, self.supplier_location)
- self.assertEqual(sum(supplier_quants.mapped('quantity')), -5.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 5.0)
- self.assertEqual(len(self.gather_relevant(self.product_serial, self.supplier_location)), 5.0)
- quants = self.gather_relevant(self.product_serial, self.stock_location)
- self.assertEqual(len(quants), 5.0)
- for quant in quants:
- self.assertNotEqual(quant.in_date, False)
- def test_out_1(self):
- """ Send products to a client. Check that a move line is created reserving products in
- stock and that the delivery correctly remove the single quant in stock.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 100)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 100.0)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_out_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'confirmed')
- # assignment
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- # Should be a reserved quantity and thus a quant.
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- # fill the move line
- move_line = move1.move_line_ids[0]
- self.assertEqual(move_line.reserved_qty, 100.0)
- self.assertEqual(move_line.qty_done, 0.0)
- move_line.qty_done = 100.0
- with self.assertRaises(UserError, msg="It should not be possible to write directly to reserved_qty"):
- move_line.reserved_qty = 1.0
- # validation
- move1._action_done()
- self.assertEqual(move1.state, 'done')
- # Check there is one quant in customer location
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.customer_location), 100.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.customer_location)), 1.0)
- # there should be no quant amymore in the stock location
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 0.0)
- def test_out_2(self):
- """ Send a consumable product to a client. Check that a move line is created but
- quants are not impacted.
- """
- # make some stock
- self.product.type = 'consu'
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_out_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- # Should be a reserved quantity and thus a quant.
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 0.0)
- # fill the move line
- move_line = move1.move_line_ids[0]
- self.assertEqual(move_line.reserved_qty, 100.0)
- self.assertEqual(move_line.qty_done, 0.0)
- move_line.qty_done = 100.0
- # validation
- move1._action_done()
- self.assertEqual(move1.state, 'done')
- # no quants are created in the customer location since it's a consumable
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.customer_location), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.customer_location)), 0.0)
- # there should be no quant amymore in the stock location
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 0.0)
- def test_mixed_tracking_reservation_1(self):
- """ Send products tracked by lot to a customer. In your stock, there are tracked and
- untracked quants. Two moves lines should be created: one for the tracked ones, another
- for the untracked ones.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_lot, self.stock_location, 2)
- self.env['stock.quant']._update_available_quantity(self.product_lot, self.stock_location, 3, lot_id=lot1)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location), 5.0)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(len(move1.move_line_ids), 2)
- def test_mixed_tracking_reservation_2(self):
- """ Send products tracked by lot to a customer. In your stock, there are two tracked and
- mulitple untracked quants. There should be as many move lines as there are quants
- reserved. Edit the reserve move lines to set them to new serial numbers, the reservation
- should stay. Validate and the final quantity in stock should be 0, not negative.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 2)
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1, lot_id=lot1)
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1, lot_id=lot2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 4.0)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 4.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(len(move1.move_line_ids), 4)
- for ml in move1.move_line_ids:
- self.assertEqual(ml.reserved_qty, 1.0)
- # assign lot3 and lot 4 to both untracked move lines
- lot3 = self.env['stock.lot'].create({
- 'name': 'lot3',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- lot4 = self.env['stock.lot'].create({
- 'name': 'lot4',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- untracked_move_line = move1.move_line_ids.filtered(lambda ml: not ml.lot_id)
- untracked_move_line[0].lot_id = lot3
- untracked_move_line[1].lot_id = lot4
- for ml in move1.move_line_ids:
- self.assertEqual(ml.reserved_qty, 1.0)
- # no changes on quants, even if i made some move lines with a lot id whom reserved on untracked quants
- self.assertEqual(len(self.gather_relevant(self.product_serial, self.stock_location, strict=True)), 1.0) # with a qty of 2
- self.assertEqual(len(self.gather_relevant(self.product_serial, self.stock_location, lot_id=lot1, strict=True).filtered(lambda q: q.lot_id)), 1.0)
- self.assertEqual(len(self.gather_relevant(self.product_serial, self.stock_location, lot_id=lot2, strict=True).filtered(lambda q: q.lot_id)), 1.0)
- self.assertEqual(len(self.gather_relevant(self.product_serial, self.stock_location, lot_id=lot3, strict=True).filtered(lambda q: q.lot_id)), 0)
- self.assertEqual(len(self.gather_relevant(self.product_serial, self.stock_location, lot_id=lot4, strict=True).filtered(lambda q: q.lot_id)), 0)
- move1.move_line_ids.write({'qty_done': 1.0})
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot1, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot2, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot3, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot4, strict=True), 0.0)
- def test_mixed_tracking_reservation_3(self):
- """ Send two products tracked by lot to a customer. In your stock, there two tracked quants
- and two untracked. Once the move is validated, add move lines to also move the two untracked
- ones and assign them serial numbers on the fly. The final quantity in stock should be 0, not
- negative.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1, lot_id=lot1)
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1, lot_id=lot2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 2.0)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.write({'qty_done': 1.0})
- move1._action_done()
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 2)
- lot3 = self.env['stock.lot'].create({
- 'name': 'lot3',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- lot4 = self.env['stock.lot'].create({
- 'name': 'lot4',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.move.line'].create({
- 'move_id': move1.id,
- 'product_id': move1.product_id.id,
- 'qty_done': 1,
- 'product_uom_id': move1.product_uom.id,
- 'location_id': move1.location_id.id,
- 'location_dest_id': move1.location_dest_id.id,
- 'lot_id': lot3.id,
- })
- self.env['stock.move.line'].create({
- 'move_id': move1.id,
- 'product_id': move1.product_id.id,
- 'qty_done': 1,
- 'product_uom_id': move1.product_uom.id,
- 'location_id': move1.location_id.id,
- 'location_dest_id': move1.location_dest_id.id,
- 'lot_id': lot4.id
- })
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot1, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot2, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot3, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot4, strict=True), 0.0)
- def test_mixed_tracking_reservation_4(self):
- """ Send two products tracked by lot to a customer. In your stock, there two tracked quants
- and on untracked. Once the move is validated, edit one of the done move line to change the
- serial number to one that is not in stock. The original serial should go back to stock and
- the untracked quant should be tracked on the fly and sent instead.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1, lot_id=lot1)
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1, lot_id=lot2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 2.0)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.write({'qty_done': 1.0})
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot1, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot2, strict=True), 0.0)
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1)
- lot3 = self.env['stock.lot'].create({
- 'name': 'lot3',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- move1.move_line_ids[1].lot_id = lot3
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot1, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot2, strict=True), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot3, strict=True), 0.0)
- def test_mixed_tracking_reservation_5(self):
- move1 = self.env['stock.move'].create({
- 'name': 'test_jenaimarre_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'confirmed')
- # create an untracked quant
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1.0)
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- # create a new move line with a lot not assigned to any quant
- self.env['stock.move.line'].create({
- 'move_id': move1.id,
- 'product_id': move1.product_id.id,
- 'qty_done': 1,
- 'product_uom_id': move1.product_uom.id,
- 'location_id': move1.location_id.id,
- 'location_dest_id': move1.location_dest_id.id,
- 'lot_id': lot1.id
- })
- self.assertEqual(len(move1.move_line_ids), 1)
- self.assertEqual(move1.reserved_availability, 0)
- # validating the move line should move the lot, not create a negative quant in stock
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product_serial, self.stock_location)), 0.0)
- def test_mixed_tracking_reservation_6(self):
- # create an untracked quant
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_jenaimarre_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- move_line = move1.move_line_ids
- move_line.lot_id = lot1
- self.assertEqual(move_line.reserved_qty, 1.0)
- move_line.lot_id = lot2
- self.assertEqual(move_line.reserved_qty, 1.0)
- move_line.qty_done = 1
- # validating the move line should move the lot, not create a negative quant in stock
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product_serial, self.stock_location)), 0.0)
- def test_mixed_tracking_reservation_7(self):
- """ Similar test_mixed_tracking_reservation_2 but creates first the tracked quant, then the
- untracked ones. When adding a lot to the untracked move line, it should not decrease the
- untracked quant then increase a non-existing tracked one that will fallback on the
- untracked quant.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1, lot_id=lot1)
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 2.0)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(len(move1.move_line_ids), 2)
- for ml in move1.move_line_ids:
- self.assertEqual(ml.reserved_qty, 1.0)
- untracked_move_line = move1.move_line_ids.filtered(lambda ml: not ml.lot_id).lot_id = lot2
- for ml in move1.move_line_ids:
- self.assertEqual(ml.reserved_qty, 1.0)
- move1.move_line_ids.write({'qty_done': 1.0})
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot1, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot2, strict=True), 0.0)
- quants = self.gather_relevant(self.product_serial, self.stock_location)
- self.assertEqual(len(quants), 0)
- def test_mixed_tracking_reservation_8(self):
- """ Send one product tracked by lot to a customer. In your stock, there are one tracked and
- one untracked quant. Reserve the move, then edit the lot to one not present in stock. The
- system will update the reservation and use the untracked quant. Now unreserve, no error
- should happen
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- # at first, we only make the tracked quant available in stock to make sure this one is selected
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1, lot_id=lot1)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_mixed_tracking_reservation_7',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.reserved_availability, 1.0)
- self.assertEqual(move1.move_line_ids.lot_id.id, lot1.id)
- # change the lot_id to one not available in stock while an untracked quant is available
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1)
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- move1.move_line_ids.lot_id = lot2
- self.assertEqual(move1.reserved_availability, 1.0)
- self.assertEqual(move1.move_line_ids.lot_id.id, lot2.id)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot1, strict=True), 1.0)
- # unreserve
- move1._do_unreserve()
- self.assertEqual(move1.reserved_availability, 0.0)
- self.assertEqual(len(move1.move_line_ids), 0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, strict=True), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.stock_location, lot_id=lot1, strict=True), 2.0)
- def test_putaway_1(self):
- """ Receive products from a supplier. Check that putaway rules are rightly applied on
- the receipt move line.
- """
- # This test will apply a putaway strategy on the stock location to put everything
- # incoming in the sublocation shelf1.
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- # putaway from stock to shelf1
- putaway = self.env['stock.putaway.rule'].create({
- 'category_id': self.env.ref('product.product_category_all').id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': shelf1_location.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)]
- })
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf1_location.id)
- def test_putaway_2(self):
- """ Receive products from a supplier. Check that putaway rules are rightly applied on
- the receipt move line.
- """
- # This test will apply a putaway strategy by product on the stock location to put everything
- # incoming in the sublocation shelf1.
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- # putaway from stock to shelf1
- putaway = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': shelf1_location.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_2',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf1_location.id)
- def test_putaway_3(self):
- """ Receive products from a supplier. Check that putaway rules are rightly applied on
- the receipt move line.
- """
- # This test will apply both the putaway strategy by product and category. We check here
- # that the putaway by product takes precedence.
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf2',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- putaway_category = self.env['stock.putaway.rule'].create({
- 'category_id': self.env.ref('product.product_category_all').id,
- 'location_in_id': self.supplier_location.id,
- 'location_out_id': shelf1_location.id,
- })
- putaway_product = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.supplier_location.id,
- 'location_out_id': shelf2_location.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(6, 0, [
- putaway_category.id,
- putaway_product.id
- ])],
- })
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_3',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf2_location.id)
- def test_putaway_4(self):
- """ Receive products from a supplier. Check that putaway rules are rightly applied on
- the receipt move line.
- """
- # This test will apply both the putaway strategy by product and category. We check here
- # that if a putaway by product is not matched, the fallback to the category is correctly
- # done.
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf2',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- # putaway from stock to shelf1
- putaway_category = self.env['stock.putaway.rule'].create({
- 'category_id': self.env.ref('product.product_category_all').id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': shelf1_location.id,
- })
- putaway_product = self.env['stock.putaway.rule'].create({
- 'product_id': self.product_consu.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': shelf2_location.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(6, 0, [
- putaway_category.id,
- putaway_product.id,
- ])],
- })
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_4',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf1_location.id)
- def test_putaway_5(self):
- """ Receive products from a supplier. Check that putaway rules are rightly applied on
- the receipt move line.
- """
- # This test will apply putaway strategy by category.
- # We check here that the putaway by category works when the category is
- # set on parent category of the product.
- shelf_location = self.env['stock.location'].create({
- 'name': 'shelf',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- putaway = self.env['stock.putaway.rule'].create({
- 'category_id': self.env.ref('product.product_category_all').id,
- 'location_in_id': self.supplier_location.id,
- 'location_out_id': shelf_location.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(6, 0, [
- putaway.id,
- ])],
- })
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_5',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf_location.id)
- def test_putaway_6(self):
- """ Receive products from a supplier. Check that putaway rules are rightly applied on
- the receipt move line.
- """
- # This test will apply two putaway strategies by category. We check here
- # that the most specific putaway takes precedence.
- child_category = self.env['product.category'].create({
- 'name': 'child_category',
- 'parent_id': self.ref('product.product_category_all'),
- })
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf2',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- putaway_category_all = self.env['stock.putaway.rule'].create({
- 'category_id': self.env.ref('product.product_category_all').id,
- 'location_in_id': self.supplier_location.id,
- 'location_out_id': shelf1_location.id,
- })
- putaway_category_office_furn = self.env['stock.putaway.rule'].create({
- 'category_id': child_category.id,
- 'location_in_id': self.supplier_location.id,
- 'location_out_id': shelf2_location.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(6, 0, [
- putaway_category_all.id,
- putaway_category_office_furn.id,
- ])],
- })
- self.product.categ_id = child_category
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_6',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf2_location.id)
- def test_putaway_7(self):
- """
- Putaway with one package type and one product
- """
- warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
- warehouse.reception_steps = 'two_steps'
- child_loc = self.stock_location.child_ids[0]
- package_type = self.env['stock.package.type'].create({
- 'name': 'Super Package Type',
- })
- package = self.env['stock.quant.package'].create({'package_type_id': package_type.id})
- self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'package_type_ids': [(6, 0, package_type.ids)],
- 'location_in_id': self.stock_location.id,
- 'location_out_id': child_loc.id,
- })
- move_input = self.env['stock.move'].create({
- 'name': self.product.name,
- 'location_id': self.supplier_location.id,
- 'location_dest_id': warehouse.wh_input_stock_loc_id.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'warehouse_id': warehouse.id,
- })
- move_input._action_confirm()
- move_input.move_line_ids.qty_done = 1
- move_input.move_line_ids.result_package_id = package
- move_input._action_done()
- move_stock = move_input.move_dest_ids
- self.assertEqual(move_stock.move_line_ids.location_dest_id, child_loc)
- def test_putaway_8(self):
- """
- Putaway with product P
- Receive 1 x P in a package with a specific type
- """
- warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
- warehouse.reception_steps = 'two_steps'
- child_loc = self.stock_location.child_ids[0]
- package_type = self.env['stock.package.type'].create({
- 'name': 'Super Package Type',
- })
- package = self.env['stock.quant.package'].create({'package_type_id': package_type.id})
- self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': child_loc.id,
- })
- move_input = self.env['stock.move'].create({
- 'name': self.product.name,
- 'location_id': self.supplier_location.id,
- 'location_dest_id': warehouse.wh_input_stock_loc_id.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'warehouse_id': warehouse.id,
- })
- move_input._action_confirm()
- move_input.move_line_ids.qty_done = 1
- move_input.move_line_ids.result_package_id = package
- move_input._action_done()
- move_stock = move_input.move_dest_ids
- self.assertEqual(move_stock.move_line_ids.location_dest_id, child_loc)
- def test_putaway_9(self):
- """
- Putaway with one category C
- 2 steps receive
- Receive one C-type product in a package with a specific type
- The putaway should be selected
- """
- warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
- warehouse.reception_steps = 'two_steps'
- basic_category = self.env.ref('product.product_category_all')
- child_locations = self.env['stock.location']
- categs = self.env['product.category']
- for i in range(3):
- loc = self.env['stock.location'].create({
- 'name': 'shelf %s' % i,
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- child_locations |= loc
- categ = self.env['product.category'].create({
- 'name': 'Category %s' % i,
- 'parent_id': basic_category.id
- })
- categs |= categ
- self.env['stock.putaway.rule'].create({
- 'category_id': categ.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': loc.id,
- })
- second_child_location = child_locations[1]
- second_categ = categs[1]
- self.product.categ_id = second_categ
- package_type = self.env['stock.package.type'].create({
- 'name': 'Super Package Type',
- })
- package = self.env['stock.quant.package'].create({
- 'package_type_id': package_type.id,
- })
- move_input = self.env['stock.move'].create({
- 'name': self.product.name,
- 'location_id': self.supplier_location.id,
- 'location_dest_id': warehouse.wh_input_stock_loc_id.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'warehouse_id': warehouse.id,
- })
- move_input._action_confirm()
- move_input.move_line_ids.qty_done = 1
- move_input.move_line_ids.result_package_id = package
- move_input._action_done()
- move_stock = move_input.move_dest_ids
- self.assertEqual(move_stock.move_line_ids.location_dest_id, second_child_location)
- def test_putaway_with_storage_category_1(self):
- """Receive a product. Test the product will be move to a child location
- with correct storage category.
- """
- # storage category
- storage_category = self.env['stock.storage.category'].create({
- 'name': "storage category"
- })
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf2',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, shelf1_location, 1.0)
- # putaway from stock to child location with storage_category
- putaway = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_move_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf2_location.id)
- def test_putaway_with_storage_category_2(self):
- """Receive a product twice. Test first time the putaway applied since we
- have enough space, and second time it is not since the location is full.
- """
- storage_category = self.env['stock.storage.category'].create({
- 'name': "storage category"
- })
- # set the capacity for the product in this storage category to be 100
- storage_category_form = Form(storage_category, view='stock.stock_storage_category_form')
- with storage_category_form.product_capacity_ids.new() as line:
- line.product_id = self.product
- line.quantity = 100
- storage_category = storage_category_form.save()
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- # putaway from stock to child location with storage_category
- putaway = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- # first move
- move1 = self.env['stock.move'].create({
- 'name': 'test_move_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf1_location.id)
- # second move
- move2 = self.env['stock.move'].create({
- 'name': 'test_move_2',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move2._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move2.move_line_ids), 1)
- # check if the putaway wasn't applied
- self.assertEqual(move2.move_line_ids.location_dest_id.id, self.stock_location.id)
- def test_putaway_with_storage_category_3(self):
- """Received products twice, set storage category to only accept new
- product when empty. Check the first time putaway rule applied and second
- time not.
- """
- storage_category = self.env['stock.storage.category'].create({
- 'name': "storage category",
- 'allow_new_product': "empty",
- })
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- # putaway from stock to child location with storage_category
- putaway = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- # first move
- move1 = self.env['stock.move'].create({
- 'name': 'test_move_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- move_line = move1.move_line_ids[0]
- move_line.qty_done = 100
- move1._action_done()
- self.assertEqual(move1.state, 'done')
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf1_location.id)
- # second move
- move2 = self.env['stock.move'].create({
- 'name': 'test_move_2',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move2._action_confirm()
- self.assertEqual(move2.state, 'assigned')
- self.assertEqual(len(move2.move_line_ids), 1)
- # check if the putaway wasn't applied
- self.assertEqual(move2.move_line_ids.location_dest_id.id, self.stock_location.id)
- def test_putaway_with_storage_category_4(self):
- """Received products, set storage category to only accept same product.
- Check the putaway rule can't be applied when the location has different
- products.
- """
- storage_category = self.env['stock.storage.category'].create({
- 'name': "storage category",
- 'allow_new_product': "same",
- })
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- # putaway from stock to child location with storage_category
- putaway = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- # create a different product and its quant
- product2 = self.env['product.product'].create({
- 'name': 'Product 2',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- self.env['stock.quant'].create({
- 'product_id': product2.id,
- 'product_uom_id': self.uom_unit.id,
- 'location_id': shelf1_location.id,
- 'quantity': 1,
- 'reserved_quantity': 0,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_move_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- move_line = move1.move_line_ids[0]
- move_line.qty_done = 100
- move1._action_done()
- self.assertEqual(move1.state, 'done')
- # check if the putaway can't be applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, self.stock_location.id)
- def test_putaway_with_storage_category_5(self):
- """Receive a package. Test the package will be move to a child location
- with correct storage category.
- """
- # Required for `result_package_id` to be visible in the view
- self.env.user.groups_id += self.env.ref("stock.group_tracking_lot")
- # storage category
- storage_category = self.env['stock.storage.category'].create({
- 'name': "storage category"
- })
- package_type = self.env['stock.package.type'].create({
- 'name': "package type",
- })
- self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf2',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- # putaway from stock to child location with storage_category
- putaway = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- 'package_type_ids': [(4, package_type.id, 0)],
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- package = self.env['stock.quant.package'].create({
- 'name': 'package',
- 'package_type_id': package_type.id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_move_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- move_form = Form(move1, view='stock.view_stock_move_nosuggest_operations')
- with move_form.move_line_nosuggest_ids.new() as line:
- line.result_package_id = package
- line.qty_done = 100
- move1 = move_form.save()
- move1._action_done()
- # check if the putaway was rightly applied
- self.assertEqual(package.location_id.id, shelf2_location.id)
- def test_putaway_with_storage_category_6(self):
- """Receive package with same package type twice. Check putaway rule can
- be applied on the first one but not the second one due to no space.
- """
- # Required for `result_package_id` to be visible in the view
- self.env.user.groups_id += self.env.ref("stock.group_tracking_lot")
- # storage category
- storage_category = self.env['stock.storage.category'].create({
- 'name': "storage category"
- })
- package_type = self.env['stock.package.type'].create({
- 'name': "package type",
- })
- # set the capacity for the package type in this storage category to be 1
- storage_category_form = Form(storage_category, view='stock.stock_storage_category_form')
- with storage_category_form.package_capacity_ids.new() as line:
- line.package_type_id = package_type
- line.quantity = 1
- storage_category = storage_category_form.save()
- self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf2',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- # putaway from stock to child location with storage_category
- putaway = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- 'package_type_ids': [(4, package_type.id, 0)],
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- # first package
- package1 = self.env['stock.quant.package'].create({
- 'name': 'package 1',
- 'package_type_id': package_type.id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_move_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- move_form = Form(move1, view='stock.view_stock_move_nosuggest_operations')
- with move_form.move_line_nosuggest_ids.new() as line:
- line.result_package_id = package1
- line.qty_done = 100
- move1 = move_form.save()
- move1._action_done()
- # check if the putaway was rightly applied
- self.assertEqual(package1.location_id.id, shelf2_location.id)
- # second package
- package2 = self.env['stock.quant.package'].create({
- 'name': 'package 2',
- 'package_type_id': package_type.id,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_move_2',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move2._action_confirm()
- self.assertEqual(move2.state, 'assigned')
- self.assertEqual(len(move2.move_line_ids), 1)
- move_form = Form(move2, view='stock.view_stock_move_nosuggest_operations')
- with move_form.move_line_nosuggest_ids.new() as line:
- line.result_package_id = package2
- line.qty_done = 100
- move2 = move_form.save()
- move2._action_done()
- # check if the putaway wasn't applied
- self.assertEqual(package2.location_id.id, self.stock_location.id)
- def test_putaway_with_storage_category_7(self):
- """Receive package with same package type twice, set storage category to
- only accept new product when empty. Check putaway rule can be applied on
- the first one but not the second one.
- """
- # Required for `result_package_id` to be visible in the view
- self.env.user.groups_id += self.env.ref("stock.group_tracking_lot")
- # storage category
- storage_category = self.env['stock.storage.category'].create({
- 'name': "storage category",
- 'allow_new_product': "empty",
- })
- package_type = self.env['stock.package.type'].create({
- 'name': "package type",
- })
- # set the capacity for the package type in this storage category to be 100
- storage_category_form = Form(storage_category, view='stock.stock_storage_category_form')
- with storage_category_form.package_capacity_ids.new() as line:
- line.package_type_id = package_type
- line.quantity = 100
- storage_category = storage_category_form.save()
- self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf2',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- # putaway from stock to child location with storage_category
- putaway = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- 'package_type_ids': [(4, package_type.id, 0)],
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- # first package
- package1 = self.env['stock.quant.package'].create({
- 'name': 'package 1',
- 'package_type_id': package_type.id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_move_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- move_form = Form(move1, view='stock.view_stock_move_nosuggest_operations')
- with move_form.move_line_nosuggest_ids.new() as line:
- line.result_package_id = package1
- line.qty_done = 100
- move1 = move_form.save()
- move1._action_done()
- # check if the putaway was rightly applied
- self.assertEqual(package1.location_id.id, shelf2_location.id)
- # second package
- package2 = self.env['stock.quant.package'].create({
- 'name': 'package 2',
- 'package_type_id': package_type.id,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_move_2',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move2._action_confirm()
- self.assertEqual(move2.state, 'assigned')
- self.assertEqual(len(move2.move_line_ids), 1)
- move_form = Form(move2, view='stock.view_stock_move_nosuggest_operations')
- with move_form.move_line_nosuggest_ids.new() as line:
- line.result_package_id = package2
- line.qty_done = 100
- move2 = move_form.save()
- move2._action_done()
- # check if the putaway wasn't applied
- self.assertEqual(package2.location_id.id, self.stock_location.id)
- def test_putaway_with_storage_category_8(self):
- """Receive package withs different products, set storage category to only
- accept same product. Check putaway rule can be applied on the first one
- but not the second one.
- """
- # Required for `result_package_id` to be visible in the view
- self.env.user.groups_id += self.env.ref("stock.group_tracking_lot")
- # storage category
- storage_category = self.env['stock.storage.category'].create({
- 'name': "storage category",
- 'allow_new_product': "same",
- })
- package_type = self.env['stock.package.type'].create({
- 'name': "package type",
- })
- # set the capacity for the package type in this storage category to be 100
- storage_category_form = Form(storage_category, view='stock.stock_storage_category_form')
- with storage_category_form.package_capacity_ids.new() as line:
- line.package_type_id = package_type
- line.quantity = 100
- storage_category = storage_category_form.save()
- self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf2',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- # putaway from stock to child location for package type
- putaway = self.env['stock.putaway.rule'].create({
- 'location_in_id': self.stock_location.id,
- 'location_out_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- 'package_type_ids': [(4, package_type.id, 0)],
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- # first package
- package1 = self.env['stock.quant.package'].create({
- 'name': 'package 1',
- 'package_type_id': package_type.id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_move_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- move_form = Form(move1, view='stock.view_stock_move_nosuggest_operations')
- with move_form.move_line_nosuggest_ids.new() as line:
- line.result_package_id = package1
- line.qty_done = 100
- move1 = move_form.save()
- move1._action_done()
- # check if the putaway was rightly applied
- self.assertEqual(package1.location_id.id, shelf2_location.id)
- # second package
- package2 = self.env['stock.quant.package'].create({
- 'name': 'package 2',
- 'package_type_id': package_type.id,
- })
- product2 = self.env['product.product'].create({
- 'name': 'Product 2',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_move_2',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': product2.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move2._action_confirm()
- self.assertEqual(move2.state, 'assigned')
- self.assertEqual(len(move2.move_line_ids), 1)
- move_form = Form(move2, view='stock.view_stock_move_nosuggest_operations')
- with move_form.move_line_nosuggest_ids.new() as line:
- line.result_package_id = package2
- line.qty_done = 100
- move2 = move_form.save()
- move2._action_done()
- # check if the putaway wasn't applied
- self.assertEqual(package2.location_id.id, self.stock_location.id)
- def test_putaway_with_storage_category_9(self):
- """Receive a product twice. Test first time the putaway applied, and second
- time it is not since the products violate the max_weight limitaion.
- """
- self.product.weight = 1
- storage_category = self.env['stock.storage.category'].create({
- 'name': "storage category",
- 'max_weight': 100,
- })
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- # putaway from stock to child location with storage_category
- putaway = self.env['stock.putaway.rule'].create({
- 'product_id': self.product.id,
- 'location_in_id': self.stock_location.id,
- 'location_out_id': self.stock_location.id,
- 'storage_category_id': storage_category.id,
- })
- self.stock_location.write({
- 'putaway_rule_ids': [(4, putaway.id, 0)],
- })
- # first move
- move1 = self.env['stock.move'].create({
- 'name': 'test_move_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- # check if the putaway was rightly applied
- self.assertEqual(move1.move_line_ids.location_dest_id.id, shelf1_location.id)
- # second move
- move2 = self.env['stock.move'].create({
- 'name': 'test_move_2',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- move2._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move2.move_line_ids), 1)
- # check if the putaway wasn't applied since there are already 100kg products in the location
- self.assertEqual(move2.move_line_ids.location_dest_id.id, self.stock_location.id)
- def test_availability_1(self):
- """ Check that the `availability` field on a move is correctly computed when there is
- more than enough products in stock.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 150.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.supplier_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 150.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- self.assertEqual(move1.availability, 100.0)
- def test_availability_2(self):
- """ Check that the `availability` field on a move is correctly computed when there is
- not enough products in stock.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 50.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.supplier_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 50.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- self.assertEqual(move1.availability, 50.0)
- def test_availability_3(self):
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, -1.0, lot_id=lot1)
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1.0, lot_id=lot2)
- move1 = self.env['stock.move'].create({
- 'name': 'test_availability_3',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(move1.reserved_availability, 1.0)
- def test_availability_4(self):
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 30.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_availability_4',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 15.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- move2 = self.env['stock.move'].create({
- 'name': 'test_availability_4',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 15.0,
- })
- move2._action_confirm()
- move2._action_assign()
- # set 15 as quantity done for the first and 30 as the second
- move1.move_line_ids.qty_done = 15
- move2.move_line_ids.qty_done = 30
- # validate the second, the first should be unreserved
- move2._action_done()
- self.assertEqual(move1.state, 'confirmed')
- self.assertEqual(move1.move_line_ids.qty_done, 15)
- self.assertEqual(move2.state, 'done')
- stock_quants = self.gather_relevant(self.product, self.stock_location)
- self.assertEqual(len(stock_quants), 0)
- customer_quants = self.gather_relevant(self.product, self.customer_location)
- self.assertEqual(customer_quants.quantity, 30)
- self.assertEqual(customer_quants.reserved_quantity, 0)
- def test_availability_5(self):
- """ Check that rerun action assign only create new stock move
- lines instead of adding quantity in existing one.
- """
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 2.0)
- # move from shelf1
- move = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 4.0,
- })
- move._action_confirm()
- move._action_assign()
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 4.0)
- move._action_assign()
- self.assertEqual(len(move.move_line_ids), 4.0)
- def test_availability_6(self):
- """ Check that, in the scenario where a move is in a bigger uom than the uom of the quants
- and this uom only allows entire numbers, we don't make a partial reservation when the
- quantity available is not enough to reserve the move. Check also that it is not possible
- to set `quantity_done` with a value not honouring the UOM's rounding.
- """
- # on the dozen uom, set the rounding set 1.0
- self.uom_dozen.rounding = 1
- # 6 units are available in stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 6.0)
- # the move should not be reserved
- move = self.env['stock.move'].create({
- 'name': 'test_availability_6',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1,
- })
- move._action_confirm()
- move._action_assign()
- self.assertEqual(move.state, 'confirmed')
- # the quants should be left untouched
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 6.0)
- # make 8 units available, the move should again not be reservabale
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 2.0)
- move._action_assign()
- self.assertEqual(move.state, 'confirmed')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 8.0)
- # make 12 units available, this time the move should be reservable
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 4.0)
- move._action_assign()
- self.assertEqual(move.state, 'assigned')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- # Check it isn't possible to set any value to quantity_done
- with self.assertRaises(UserError):
- move.quantity_done = 0.1
- move._action_done()
- with self.assertRaises(UserError):
- move.quantity_done = 1.1
- move._action_done()
- with self.assertRaises(UserError):
- move.quantity_done = 0.9
- move._action_done()
- move.quantity_done = 1
- move._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.customer_location), 12.0)
- def test_availability_7(self):
- """ Check that, in the scenario where a move is in a bigger uom than the uom of the quants
- and this uom only allows entire numbers, we only reserve quantity honouring the uom's
- rounding even if the quantity is set across multiple quants.
- """
- # on the dozen uom, set the rounding set 1.0
- self.uom_dozen.rounding = 1
- # make 12 quants of 1
- for i in range(1, 13):
- lot_id = self.env['stock.lot'].create({
- 'name': 'lot%s' % str(i),
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1.0, lot_id=lot_id)
- # the move should be reserved
- move = self.env['stock.move'].create({
- 'name': 'test_availability_7',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1,
- })
- move._action_confirm()
- move._action_assign()
- self.assertEqual(move.state, 'assigned')
- self.assertEqual(len(move.move_line_ids.mapped('product_uom_id')), 1)
- self.assertEqual(move.move_line_ids.mapped('product_uom_id'), self.uom_unit)
- for move_line in move.move_line_ids:
- move_line.qty_done = 1
- move._action_done()
- self.assertEqual(move.product_uom_qty, 1)
- self.assertEqual(move.product_uom.id, self.uom_dozen.id)
- self.assertEqual(move.state, 'done')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.customer_location), 12.0)
- self.assertEqual(len(self.gather_relevant(self.product_serial, self.customer_location)), 12)
- def test_availability_8(self):
- """ Test the assignment mechanism when the product quantity is decreased on a partially
- reserved stock move.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 3.0)
- self.assertAlmostEqual(self.product.qty_available, 3.0)
- move_partial = self.env['stock.move'].create({
- 'name': 'test_partial',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- })
- move_partial._action_confirm()
- move_partial._action_assign()
- self.assertAlmostEqual(self.product.virtual_available, -2.0)
- self.assertEqual(move_partial.state, 'partially_available')
- move_partial.product_uom_qty = 3.0
- move_partial._action_assign()
- self.assertEqual(move_partial.state, 'assigned')
- def test_availability_9(self):
- """ Test the assignment mechanism when the product quantity is increase
- on a receipt move.
- """
- move_receipt = self.env['stock.move'].create({
- 'name': 'test_receipt_edit',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1.0,
- })
- move_receipt._action_confirm()
- move_receipt._action_assign()
- self.assertEqual(move_receipt.state, 'assigned')
- move_receipt.product_uom_qty = 3.0
- move_receipt._action_assign()
- self.assertEqual(move_receipt.state, 'assigned')
- self.assertEqual(move_receipt.move_line_ids.reserved_uom_qty, 3)
- def test_unreserve_1(self):
- """ Check that unreserving a stock move sets the products reserved as available and
- set the state back to confirmed.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 150.0)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.supplier_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 150.0)
- self.assertEqual(move1.availability, 100.0)
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'confirmed')
- # assignment
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 50.0)
- # unreserve
- move1._do_unreserve()
- self.assertEqual(len(move1.move_line_ids), 0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 150.0)
- self.assertEqual(move1.state, 'confirmed')
- def test_unreserve_2(self):
- """ Check that unreserving a stock move sets the products reserved as available and
- set the state back to confirmed even if they are in a pack.
- """
- package1 = self.env['stock.quant.package'].create({'name': 'test_unreserve_2_pack'})
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 150.0, package_id=package1)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_putaway_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.supplier_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100.0,
- })
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package1), 150.0)
- self.assertEqual(move1.availability, 100.0)
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'confirmed')
- # assignment
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package1), 50.0)
- # unreserve
- move1._do_unreserve()
- self.assertEqual(len(move1.move_line_ids), 0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package1), 150.0)
- self.assertEqual(move1.state, 'confirmed')
- def test_unreserve_3(self):
- """ Similar to `test_unreserve_1` but checking the quants more in details.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 2)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_out_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'confirmed')
- # assignment
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- quants = self.gather_relevant(self.product, self.stock_location)
- self.assertEqual(len(quants), 1.0)
- self.assertEqual(quants.quantity, 2.0)
- self.assertEqual(quants.reserved_quantity, 2.0)
- move1._do_unreserve()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(len(quants), 1.0)
- self.assertEqual(quants.quantity, 2.0)
- self.assertEqual(quants.reserved_quantity, 0.0)
- self.assertEqual(len(move1.move_line_ids), 0.0)
- def test_unreserve_4(self):
- """ Check the unreservation of a partially available stock move.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 2)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_out_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 3.0,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'confirmed')
- # assignment
- move1._action_assign()
- self.assertEqual(move1.state, 'partially_available')
- self.assertEqual(len(move1.move_line_ids), 1)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- quants = self.gather_relevant(self.product, self.stock_location)
- self.assertEqual(len(quants), 1.0)
- self.assertEqual(quants.quantity, 2.0)
- self.assertEqual(quants.reserved_quantity, 2.0)
- move1._do_unreserve()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(len(quants), 1.0)
- self.assertEqual(quants.quantity, 2.0)
- self.assertEqual(quants.reserved_quantity, 0.0)
- self.assertEqual(len(move1.move_line_ids), 0.0)
- def test_unreserve_5(self):
- """ Check the unreservation of a stock move reserved on multiple quants.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 3)
- self.env['stock.quant'].create({
- 'product_id': self.product.id,
- 'location_id': self.stock_location.id,
- 'quantity': 2,
- })
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 5)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_unreserve_5',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'confirmed')
- # assignment
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- move1._do_unreserve()
- quants = self.gather_relevant(self.product, self.stock_location)
- self.assertEqual(len(quants), 2.0)
- for quant in quants:
- self.assertEqual(quant.reserved_quantity, 0)
- def test_unreserve_6(self):
- """ In a situation with a negative and a positive quant, reserve and unreserve.
- """
- q1 = self.env['stock.quant'].create({
- 'product_id': self.product.id,
- 'location_id': self.stock_location.id,
- 'quantity': -10,
- 'reserved_quantity': 0,
- })
- q2 = self.env['stock.quant'].create({
- 'product_id': self.product.id,
- 'location_id': self.stock_location.id,
- 'quantity': 30.0,
- 'reserved_quantity': 10.0,
- })
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 10.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_unreserve_6',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- self.assertEqual(move1.move_line_ids.reserved_qty, 10)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- self.assertEqual(q2.reserved_quantity, 20)
- move1._do_unreserve()
- self.assertEqual(move1.state, 'confirmed')
- self.assertEqual(len(move1.move_line_ids), 0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 10.0)
- self.assertEqual(q2.reserved_quantity, 10)
- def test_unreserve_7(self):
- """ Check the unreservation of a stock move delete only stock move lines
- without quantity done.
- """
- product = self.env['product.product'].create({
- 'name': 'product',
- 'tracking': 'serial',
- 'type': 'product',
- })
- serial_numbers = self.env['stock.lot'].create([{
- 'name': str(x),
- 'product_id': product.id,
- 'company_id': self.env.company.id,
- } for x in range(5)])
- for serial in serial_numbers:
- self.env['stock.quant'].create({
- 'product_id': product.id,
- 'location_id': self.stock_location.id,
- 'quantity': 1.0,
- 'lot_id': serial.id,
- 'reserved_quantity': 0.0,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_unreserve_7',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': product.id,
- 'product_uom': product.uom_id.id,
- 'product_uom_qty': 5.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 5)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(product, self.stock_location), 0.0)
- # Check state is changed even with 0 move lines unlinked
- move1.move_line_ids.write({'qty_done': 1})
- move1._do_unreserve()
- self.assertEqual(len(move1.move_line_ids), 5)
- self.assertEqual(move1.state, 'confirmed')
- move1._action_assign()
- # set a quantity done on the two first move lines
- move1.move_line_ids.write({'qty_done': 0})
- move1.move_line_ids[0].qty_done = 1
- move1.move_line_ids[1].qty_done = 1
- move1._do_unreserve()
- self.assertEqual(move1.state, 'confirmed')
- self.assertEqual(len(move1.move_line_ids), 2)
- self.assertEqual(move1.move_line_ids.mapped('qty_done'), [1, 1])
- self.assertEqual(move1.move_line_ids.mapped('reserved_uom_qty'), [0, 0])
- def test_link_assign_1(self):
- """ Test the assignment mechanism when two chained stock moves try to move one unit of an
- untracked product.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- move_stock_pack = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_pack_cust = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_2',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_stock_pack.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
- (move_stock_pack + move_pack_cust)._action_confirm()
- move_stock_pack._action_assign()
- move_stock_pack.move_line_ids[0].qty_done = 1.0
- move_stock_pack._action_done()
- self.assertEqual(len(move_pack_cust.move_line_ids), 1)
- move_line = move_pack_cust.move_line_ids[0]
- self.assertEqual(move_line.location_id.id, self.pack_location.id)
- self.assertEqual(move_line.location_dest_id.id, self.customer_location.id)
- self.assertEqual(move_pack_cust.state, 'assigned')
- def test_link_assign_2(self):
- """ Test the assignment mechanism when two chained stock moves try to move one unit of a
- tracked product.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot1)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location, lot1)), 1.0)
- move_stock_pack = self.env['stock.move'].create({
- 'name': 'test_link_2_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_pack_cust = self.env['stock.move'].create({
- 'name': 'test_link_2_2',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_stock_pack.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
- (move_stock_pack + move_pack_cust)._action_confirm()
- move_stock_pack._action_assign()
- move_line_stock_pack = move_stock_pack.move_line_ids[0]
- self.assertEqual(move_line_stock_pack.lot_id.id, lot1.id)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location, lot1)), 1.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.pack_location, lot1)), 0.0)
- move_line_stock_pack.qty_done = 1.0
- move_stock_pack._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location, lot1)), 0.0)
- move_line_pack_cust = move_pack_cust.move_line_ids[0]
- self.assertEqual(move_line_pack_cust.lot_id.id, lot1.id)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.pack_location, lot_id=lot1), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.pack_location, lot1)), 1.0)
- def test_link_assign_3(self):
- """ Test the assignment mechanism when three chained stock moves (2 sources, 1 dest) try to
- move multiple units of an untracked product.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 2.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- move_stock_pack_1 = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_stock_pack_2 = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_pack_cust = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_2',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move_stock_pack_1.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_stock_pack_2.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack_1.id, 0), (4, move_stock_pack_2.id, 0)]})
- (move_stock_pack_1 + move_stock_pack_2 + move_pack_cust)._action_confirm()
- # assign and fulfill the first move
- move_stock_pack_1._action_assign()
- self.assertEqual(move_stock_pack_1.state, 'assigned')
- self.assertEqual(len(move_stock_pack_1.move_line_ids), 1)
- move_stock_pack_1.move_line_ids[0].qty_done = 1.0
- move_stock_pack_1._action_done()
- self.assertEqual(move_stock_pack_1.state, 'done')
- # the destination move should be partially available and have one move line
- self.assertEqual(move_pack_cust.state, 'partially_available')
- self.assertEqual(len(move_pack_cust.move_line_ids), 1)
- # Should have 1 quant in stock_location and another in pack_location
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.pack_location)), 1.0)
- move_stock_pack_2._action_assign()
- self.assertEqual(move_stock_pack_2.state, 'assigned')
- self.assertEqual(len(move_stock_pack_2.move_line_ids), 1)
- move_stock_pack_2.move_line_ids[0].qty_done = 1.0
- move_stock_pack_2._action_done()
- self.assertEqual(move_stock_pack_2.state, 'done')
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.pack_location)), 1.0)
- self.assertEqual(move_pack_cust.state, 'assigned')
- self.assertEqual(len(move_pack_cust.move_line_ids), 1)
- move_line_1 = move_pack_cust.move_line_ids[0]
- self.assertEqual(move_line_1.location_id.id, self.pack_location.id)
- self.assertEqual(move_line_1.location_dest_id.id, self.customer_location.id)
- self.assertEqual(move_line_1.reserved_qty, 2.0)
- self.assertEqual(move_pack_cust.state, 'assigned')
- def test_link_assign_4(self):
- """ Test the assignment mechanism when three chained stock moves (2 sources, 1 dest) try to
- move multiple units of a tracked by lot product.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 2.0, lot_id=lot1)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location, lot1)), 1.0)
- move_stock_pack_1 = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_stock_pack_2 = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_pack_cust = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_2',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move_stock_pack_1.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_stock_pack_2.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack_1.id, 0), (4, move_stock_pack_2.id, 0)]})
- (move_stock_pack_1 + move_stock_pack_2 + move_pack_cust)._action_confirm()
- # assign and fulfill the first move
- move_stock_pack_1._action_assign()
- self.assertEqual(len(move_stock_pack_1.move_line_ids), 1)
- self.assertEqual(move_stock_pack_1.move_line_ids[0].lot_id.id, lot1.id)
- move_stock_pack_1.move_line_ids[0].qty_done = 1.0
- move_stock_pack_1._action_done()
- # the destination move should be partially available and have one move line
- self.assertEqual(len(move_pack_cust.move_line_ids), 1)
- move_stock_pack_2._action_assign()
- self.assertEqual(len(move_stock_pack_2.move_line_ids), 1)
- self.assertEqual(move_stock_pack_2.move_line_ids[0].lot_id.id, lot1.id)
- move_stock_pack_2.move_line_ids[0].qty_done = 1.0
- move_stock_pack_2._action_done()
- self.assertEqual(len(move_pack_cust.move_line_ids), 1)
- move_line_1 = move_pack_cust.move_line_ids[0]
- self.assertEqual(move_line_1.location_id.id, self.pack_location.id)
- self.assertEqual(move_line_1.location_dest_id.id, self.customer_location.id)
- self.assertEqual(move_line_1.reserved_qty, 2.0)
- self.assertEqual(move_line_1.lot_id.id, lot1.id)
- self.assertEqual(move_pack_cust.state, 'assigned')
- def test_link_assign_5(self):
- """ Test the assignment mechanism when three chained stock moves (1 sources, 2 dest) try to
- move multiple units of an untracked product.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 2.0)
- move_stock_pack = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move_pack_cust_1 = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_1',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_pack_cust_2 = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_2',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_stock_pack.write({'move_dest_ids': [(4, move_pack_cust_1.id, 0), (4, move_pack_cust_2.id, 0)]})
- move_pack_cust_1.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
- move_pack_cust_2.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
- (move_stock_pack + move_pack_cust_1 + move_pack_cust_2)._action_confirm()
- # assign and fulfill the first move
- move_stock_pack._action_assign()
- self.assertEqual(len(move_stock_pack.move_line_ids), 1)
- move_stock_pack.move_line_ids[0].qty_done = 2.0
- move_stock_pack._action_done()
- # the destination moves should be available and have one move line
- self.assertEqual(len(move_pack_cust_1.move_line_ids), 1)
- self.assertEqual(len(move_pack_cust_2.move_line_ids), 1)
- move_pack_cust_1.move_line_ids[0].qty_done = 1.0
- move_pack_cust_2.move_line_ids[0].qty_done = 1.0
- (move_pack_cust_1 + move_pack_cust_2)._action_done()
- def test_link_assign_6(self):
- """ Test the assignment mechanism when four chained stock moves (2 sources, 2 dest) try to
- move multiple units of an untracked by lot product. This particular test case simulates a two
- step receipts with backorder.
- """
- move_supp_stock_1 = self.env['stock.move'].create({
- 'name': 'test_link_assign_6_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 3.0,
- })
- move_supp_stock_2 = self.env['stock.move'].create({
- 'name': 'test_link_assign_6_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move_stock_stock_1 = self.env['stock.move'].create({
- 'name': 'test_link_assign_6_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 3.0,
- })
- move_stock_stock_1.write({'move_orig_ids': [(4, move_supp_stock_1.id, 0), (4, move_supp_stock_2.id, 0)]})
- move_stock_stock_2 = self.env['stock.move'].create({
- 'name': 'test_link_assign_6_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 3.0,
- })
- move_stock_stock_2.write({'move_orig_ids': [(4, move_supp_stock_1.id, 0), (4, move_supp_stock_2.id, 0)]})
- (move_supp_stock_1 + move_supp_stock_2 + move_stock_stock_1 + move_stock_stock_2)._action_confirm()
- move_supp_stock_1._action_assign()
- self.assertEqual(move_supp_stock_1.state, 'assigned')
- self.assertEqual(move_supp_stock_2.state, 'assigned')
- self.assertEqual(move_stock_stock_1.state, 'waiting')
- self.assertEqual(move_stock_stock_2.state, 'waiting')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- # do the fist move, it'll bring 3 units in stock location so only `move_stock_stock_1`
- # should be assigned
- move_supp_stock_1.move_line_ids.qty_done = 3.0
- move_supp_stock_1._action_done()
- self.assertEqual(move_supp_stock_1.state, 'done')
- self.assertEqual(move_supp_stock_2.state, 'assigned')
- self.assertEqual(move_stock_stock_1.state, 'assigned')
- self.assertEqual(move_stock_stock_2.state, 'waiting')
- def test_link_assign_7(self):
- # on the dozen uom, set the rounding set 1.0
- self.uom_dozen.rounding = 1
- # 6 units are available in stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 6.0)
- # create pickings and moves for a pick -> pack mto scenario
- picking_stock_pack = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_internal').id,
- })
- move_stock_pack = self.env['stock.move'].create({
- 'name': 'test_link_assign_7',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking_stock_pack.id,
- })
- picking_pack_cust = self.env['stock.picking'].create({
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move_pack_cust = self.env['stock.move'].create({
- 'name': 'test_link_assign_7',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking_pack_cust.id,
- })
- move_stock_pack.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
- (move_stock_pack + move_pack_cust)._action_confirm()
- # the pick should not be reservable because of the rounding of the dozen
- move_stock_pack._action_assign()
- self.assertEqual(move_stock_pack.state, 'confirmed')
- move_pack_cust._action_assign()
- self.assertEqual(move_pack_cust.state, 'waiting')
- # move the 6 units by adding an unreserved move line
- move_stock_pack.write({'move_line_ids': [(0, 0, {
- 'product_id': self.product.id,
- 'product_uom_id': self.uom_unit.id,
- 'qty_done': 6,
- 'reserved_uom_qty': 0,
- 'lot_id': False,
- 'package_id': False,
- 'result_package_id': False,
- 'location_id': move_stock_pack.location_id.id,
- 'location_dest_id': move_stock_pack.location_dest_id.id,
- 'picking_id': picking_stock_pack.id,
- })]})
- # the quantity done on the move should not respect the rounding of the move line
- self.assertEqual(move_stock_pack.quantity_done, 0.5)
- # create the backorder in the uom of the quants
- backorder_wizard_dict = picking_stock_pack.button_validate()
- backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
- backorder_wizard.process()
- self.assertEqual(move_stock_pack.state, 'done')
- self.assertEqual(move_stock_pack.quantity_done, 0.5)
- self.assertEqual(move_stock_pack.product_uom_qty, 0.5)
- # the second move should not be reservable because of the rounding on the dozen
- move_pack_cust._action_assign()
- self.assertEqual(move_pack_cust.state, 'partially_available')
- move_line_pack_cust = move_pack_cust.move_line_ids
- self.assertEqual(move_line_pack_cust.reserved_uom_qty, 6)
- self.assertEqual(move_line_pack_cust.product_uom_id.id, self.uom_unit.id)
- # move a dozen on the backorder to see how we handle the extra move
- backorder = self.env['stock.picking'].search([('backorder_id', '=', picking_stock_pack.id)])
- backorder.move_ids.write({'move_line_ids': [(0, 0, {
- 'product_id': self.product.id,
- 'product_uom_id': self.uom_dozen.id,
- 'qty_done': 1,
- 'reserved_uom_qty': 0,
- 'lot_id': False,
- 'package_id': False,
- 'result_package_id': False,
- 'location_id': backorder.location_id.id,
- 'location_dest_id': backorder.location_dest_id.id,
- 'picking_id': backorder.id,
- })]})
- backorder.button_validate()
- backorder_move = backorder.move_ids
- self.assertEqual(backorder_move.state, 'done')
- self.assertEqual(backorder_move.quantity_done, 12.0)
- self.assertEqual(backorder_move.product_uom_qty, 12.0)
- self.assertEqual(backorder_move.product_uom, self.uom_unit)
- # the second move should now be reservable
- move_pack_cust._action_assign()
- self.assertEqual(move_pack_cust.state, 'assigned')
- self.assertEqual(move_line_pack_cust.reserved_uom_qty, 12)
- self.assertEqual(move_line_pack_cust.product_uom_id.id, self.uom_unit.id)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, move_stock_pack.location_dest_id), 6)
- def test_link_assign_8(self):
- """ Set the rounding of the dozen to 1.0, create a chain of two move for a dozen, the product
- concerned is tracked by serial number. Check that the flow is ok.
- """
- # on the dozen uom, set the rounding set 1.0
- self.uom_dozen.rounding = 1
- # 6 units are available in stock
- for i in range(1, 13):
- lot_id = self.env['stock.lot'].create({
- 'name': 'lot%s' % str(i),
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1.0, lot_id=lot_id)
- # create pickings and moves for a pick -> pack mto scenario
- picking_stock_pack = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_internal').id,
- })
- move_stock_pack = self.env['stock.move'].create({
- 'name': 'test_link_assign_7',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking_stock_pack.id,
- })
- picking_pack_cust = self.env['stock.picking'].create({
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move_pack_cust = self.env['stock.move'].create({
- 'name': 'test_link_assign_7',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking_pack_cust.id,
- })
- move_stock_pack.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
- (move_stock_pack + move_pack_cust)._action_confirm()
- move_stock_pack._action_assign()
- self.assertEqual(move_stock_pack.state, 'assigned')
- move_pack_cust._action_assign()
- self.assertEqual(move_pack_cust.state, 'waiting')
- for ml in move_stock_pack.move_line_ids:
- ml.qty_done = 1
- picking_stock_pack.button_validate()
- self.assertEqual(move_pack_cust.state, 'assigned')
- for ml in move_pack_cust.move_line_ids:
- self.assertEqual(ml.reserved_uom_qty, 1)
- self.assertEqual(ml.product_uom_id.id, self.uom_unit.id)
- self.assertTrue(bool(ml.lot_id.id))
- def test_link_assign_9(self):
- """ Create an uom "3 units" which is 3 times the units but without rounding. Create 3
- quants in stock and two chained moves. The first move will bring the 3 quants but the
- second only validate 2 and create a backorder for the last one. Check that the reservation
- is correctly cleared up for the last one.
- """
- uom_3units = self.env['uom.uom'].create({
- 'name': '3 units',
- 'category_id': self.uom_unit.category_id.id,
- 'factor_inv': 3,
- 'rounding': 1,
- 'uom_type': 'bigger',
- })
- for i in range(1, 4):
- lot_id = self.env['stock.lot'].create({
- 'name': 'lot%s' % str(i),
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.stock_location, 1.0, lot_id=lot_id)
- picking_stock_pack = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_internal').id,
- })
- move_stock_pack = self.env['stock.move'].create({
- 'name': 'test_link_assign_9',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': uom_3units.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking_stock_pack.id,
- })
- picking_pack_cust = self.env['stock.picking'].create({
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move_pack_cust = self.env['stock.move'].create({
- 'name': 'test_link_assign_0',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': uom_3units.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking_pack_cust.id,
- })
- move_stock_pack.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
- (move_stock_pack + move_pack_cust)._action_confirm()
- picking_stock_pack.action_assign()
- for ml in picking_stock_pack.move_ids.move_line_ids:
- ml.qty_done = 1
- picking_stock_pack.button_validate()
- self.assertEqual(picking_pack_cust.state, 'assigned')
- for ml in picking_pack_cust.move_ids.move_line_ids:
- if ml.lot_id.name != 'lot3':
- ml.qty_done = 1
- res_dict_for_back_order = picking_pack_cust.button_validate()
- backorder_wizard = self.env[(res_dict_for_back_order.get('res_model'))].browse(res_dict_for_back_order.get('res_id')).with_context(res_dict_for_back_order['context'])
- backorder_wizard.process()
- backorder = self.env['stock.picking'].search([('backorder_id', '=', picking_pack_cust.id)])
- backordered_move = backorder.move_ids
- # due to the rounding, the backordered quantity is 0.999 ; we shoudln't be able to reserve
- # 0.999 on a tracked by serial number quant
- backordered_move._action_assign()
- self.assertEqual(backordered_move.reserved_availability, 0)
- # force the serial number and validate
- lot3 = self.env['stock.lot'].search([('name', '=', "lot3")])
- backorder.write({'move_line_ids': [(0, 0, {
- 'product_id': self.product_serial.id,
- 'product_uom_id': self.uom_unit.id,
- 'qty_done': 1,
- 'reserved_uom_qty': 0,
- 'lot_id': lot3.id,
- 'package_id': False,
- 'result_package_id': False,
- 'location_id': backordered_move.location_id.id,
- 'location_dest_id': backordered_move.location_dest_id.id,
- 'move_id': backordered_move.id,
- })]})
- backorder.button_validate()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.customer_location), 3)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_serial, self.pack_location), 0)
- def test_link_assign_10(self):
- """ Test the assignment mechanism with partial availability.
- """
- # make some stock:
- # stock location: 2.0
- # pack location: -1.0
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 2.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 1.0)
- move_out = self.env['stock.move'].create({
- 'name': 'test_link_assign_out',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_out._action_confirm()
- move_out._action_assign()
- move_out.quantity_done = 1.0
- move_out._action_done()
- self.assertEqual(len(self.gather_relevant(self.product, self.pack_location)), 1.0)
- move_stock_pack = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move_pack_cust = self.env['stock.move'].create({
- 'name': 'test_link_assign_1_2',
- 'location_id': self.pack_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move_stock_pack.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
- move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
- (move_stock_pack + move_pack_cust)._action_confirm()
- move_stock_pack._action_assign()
- move_stock_pack.quantity_done = 2.0
- move_stock_pack._action_done()
- self.assertEqual(len(move_pack_cust.move_line_ids), 1)
- self.assertAlmostEqual(move_pack_cust.reserved_availability, 1.0)
- self.assertEqual(move_pack_cust.state, 'partially_available')
- def test_use_reserved_move_line_1(self):
- """ Test that _free_reservation work when quantity is only available on
- reserved move lines.
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 10.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_use_unreserved_move_line_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_use_unreserved_move_line_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move2._action_confirm()
- move2._action_assign()
- move3 = self.env['stock.move'].create({
- 'name': 'test_use_unreserved_move_line_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 0.0,
- 'quantity_done': 1.0,
- })
- move3._action_confirm()
- move3._action_assign()
- move3._action_done()
- self.assertEqual(move3.state, 'done')
- quant = self.env['stock.quant']._gather(self.product, self.stock_location)
- self.assertEqual(quant.quantity, 9.0)
- self.assertEqual(quant.reserved_quantity, 9.0)
- def test_use_reserved_move_line_2(self):
- # make 12 units available in stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 12.0)
- # reserve 12 units
- move1 = self.env['stock.move'].create({
- 'name': 'test_use_reserved_move_line_2_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 12,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- quant = self.env['stock.quant']._gather(self.product, self.stock_location)
- self.assertEqual(quant.quantity, 12)
- self.assertEqual(quant.reserved_quantity, 12)
- # force a move of 1 dozen
- move2 = self.env['stock.move'].create({
- 'name': 'test_use_reserved_move_line_2_2',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1,
- })
- move2._action_confirm()
- move2._action_assign()
- self.assertEqual(move2.state, 'confirmed')
- move2._set_quantity_done(1)
- move2._action_done()
- # mov1 should be unreserved and the quant should be unlinked
- self.assertEqual(move1.state, 'confirmed')
- quant = self.env['stock.quant']._gather(self.product, self.stock_location)
- self.assertEqual(quant.quantity, 0)
- self.assertEqual(quant.reserved_quantity, 0)
- def test_use_unreserved_move_line_1(self):
- """ Test that validating a stock move linked to an untracked product reserved by another one
- correctly unreserves the other one.
- """
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0)
- # prepare the conflicting move
- move1 = self.env['stock.move'].create({
- 'name': 'test_use_unreserved_move_line_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_use_unreserved_move_line_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- # reserve those move
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- move2._action_confirm()
- move2._action_assign()
- self.assertEqual(move2.state, 'confirmed')
- # use the product from the first one
- move2.write({'move_line_ids': [(0, 0, {
- 'product_id': self.product.id,
- 'product_uom_id': self.uom_unit.id,
- 'qty_done': 1,
- 'reserved_uom_qty': 0,
- 'lot_id': False,
- 'package_id': False,
- 'result_package_id': False,
- 'location_id': move2.location_id.id,
- 'location_dest_id': move2.location_dest_id.id,
- })]})
- move2._action_done()
- # the first move should go back to confirmed
- self.assertEqual(move1.state, 'confirmed')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- def test_use_unreserved_move_line_2(self):
- """ Test that validating a stock move linked to a tracked product reserved by another one
- correctly unreserves the other one.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- # make some stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot1)
- # prepare the conflicting move
- move1 = self.env['stock.move'].create({
- 'name': 'test_use_unreserved_move_line_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_use_unreserved_move_line_1_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- # reserve those move
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 1.0)
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- move2._action_confirm()
- move2._action_assign()
- self.assertEqual(move2.state, 'confirmed')
- # use the product from the first one
- move2.write({'move_line_ids': [(0, 0, {
- 'product_id': self.product.id,
- 'product_uom_id': self.uom_unit.id,
- 'qty_done': 1,
- 'reserved_uom_qty': 0,
- 'lot_id': lot1.id,
- 'package_id': False,
- 'result_package_id': False,
- 'location_id': move2.location_id.id,
- 'location_dest_id': move2.location_dest_id.id,
- })]})
- move2._action_done()
- # the first move should go back to confirmed
- self.assertEqual(move1.state, 'confirmed')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 0.0)
- def test_use_unreserved_move_line_3(self):
- """ Test the behavior of `_free_reservation` when ran on a recordset of move lines where
- some are assigned and some are force assigned. `_free_reservation` should not use an
- already processed move line when looking for a move line candidate to unreserve.
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_use_unreserved_move_line_3',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 3.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.quantity_done = 1
- # add a forced move line in `move1`
- move1.write({'move_line_ids': [(0, 0, {
- 'product_id': self.product.id,
- 'product_uom_id': self.uom_unit.id,
- 'qty_done': 2,
- 'reserved_uom_qty': 0,
- 'lot_id': False,
- 'package_id': False,
- 'result_package_id': False,
- 'location_id': move1.location_id.id,
- 'location_dest_id': move1.location_dest_id.id,
- })]})
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.customer_location), 3.0)
- def test_use_unreserved_move_line_4(self):
- product_01 = self.env['product.product'].create({
- 'name': 'Product 01',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- product_02 = self.env['product.product'].create({
- 'name': 'Product 02',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- self.env['stock.quant']._update_available_quantity(product_01, self.stock_location, 1)
- self.env['stock.quant']._update_available_quantity(product_02, self.stock_location, 1)
- customer = self.env['res.partner'].create({'name': 'SuperPartner'})
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'partner_id': customer.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- p01_move = self.env['stock.move'].create({
- 'name': 'SuperMove01',
- 'location_id': picking.location_id.id,
- 'location_dest_id': picking.location_dest_id.id,
- 'picking_id': picking.id,
- 'product_id': product_01.id,
- 'product_uom_qty': 1,
- 'product_uom': product_01.uom_id.id,
- })
- self.env['stock.move'].create({
- 'name': 'SuperMove02',
- 'location_id': picking.location_id.id,
- 'location_dest_id': picking.location_dest_id.id,
- 'picking_id': picking.id,
- 'product_id': product_02.id,
- 'product_uom_qty': 1,
- 'product_uom': product_02.uom_id.id,
- })
- picking.action_confirm()
- picking.action_assign()
- p01_move.product_uom_qty = 0
- picking.do_unreserve()
- picking.action_assign()
- p01_move.product_uom_qty = 1
- self.assertEqual(p01_move.state, 'confirmed')
- def test_edit_reserved_move_line_1(self):
- """ Test that editing a stock move line linked to an untracked product correctly and
- directly adapts the reservation. In this case, we edit the sublocation where we take the
- product to another sublocation where a product is available.
- """
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, shelf1_location, 1.0)
- self.env['stock.quant']._update_available_quantity(self.product, shelf2_location, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- move1.move_line_ids.location_id = shelf2_location.id
- self.assertEqual(move1.reserved_availability, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- def test_edit_reserved_move_line_2(self):
- """ Test that editing a stock move line linked to a tracked product correctly and directly
- adapts the reservation. In this case, we edit the lot to another available one.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot1)
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.reserved_availability, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 1.0)
- move1.move_line_ids.lot_id = lot2.id
- self.assertEqual(move1.reserved_availability, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 0.0)
- def test_edit_reserved_move_line_3(self):
- """ Test that editing a stock move line linked to a packed product correctly and directly
- adapts the reservation. In this case, we edit the package to another available one.
- """
- package1 = self.env['stock.quant.package'].create({'name': 'test_edit_reserved_move_line_3'})
- package2 = self.env['stock.quant.package'].create({'name': 'test_edit_reserved_move_line_3'})
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, package_id=package1)
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, package_id=package2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package2), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package2), 1.0)
- move1.move_line_ids.package_id = package2.id
- self.assertEqual(move1.reserved_availability, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package2), 0.0)
- def test_edit_reserved_move_line_4(self):
- """ Test that editing a stock move line linked to an owned product correctly and directly
- adapts the reservation. In this case, we edit the owner to another available one.
- """
- owner1 = self.env['res.partner'].create({'name': 'test_edit_reserved_move_line_4_1'})
- owner2 = self.env['res.partner'].create({'name': 'test_edit_reserved_move_line_4_2'})
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, owner_id=owner1)
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, owner_id=owner2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner2), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner2), 1.0)
- move1.move_line_ids.owner_id = owner2.id
- self.assertEqual(move1.reserved_availability, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner2), 0.0)
- def test_edit_reserved_move_line_5(self):
- """ Test that editing a stock move line linked to a packed and tracked product correctly
- and directly adapts the reservation. In this case, we edit the lot to another available one
- that is not in a pack.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- package1 = self.env['stock.quant.package'].create({'name': 'test_edit_reserved_move_line_5'})
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot1, package_id=package1)
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1, package_id=package1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1, package_id=package1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 1.0)
- move_line = move1.move_line_ids[0]
- move_line.write({'package_id': False, 'lot_id': lot2.id})
- self.assertEqual(move1.reserved_availability, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1, package_id=package1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 0.0)
- def test_edit_reserved_move_line_6(self):
- """ Test that editing a stock move line linked to an untracked product correctly and
- directly adapts the reservation. In this case, we edit the sublocation where we take the
- product to another sublocation where a product is NOT available.
- """
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, shelf1_location, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.move_line_ids.state, 'assigned')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- move1.move_line_ids.location_id = shelf2_location.id
- self.assertEqual(move1.move_line_ids.state, 'confirmed')
- self.assertEqual(move1.reserved_availability, 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- def test_edit_reserved_move_line_7(self):
- """ Send 5 tracked products to a client, but these products do not have any lot set in our
- inventory yet: we only set them at delivery time. The created move line should have 5 items
- without any lot set, if we edit to set them to lot1, the reservation should not change.
- Validating the stock move should should not create a negative quant for this lot in stock
- location.
- # """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- # make some stock without assigning a lot id
- self.env['stock.quant']._update_available_quantity(self.product_lot, self.stock_location, 5)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'confirmed')
- # assignment
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- move_line = move1.move_line_ids[0]
- self.assertEqual(move_line.reserved_qty, 5)
- move_line.qty_done = 5.0
- self.assertEqual(move_line.reserved_qty, 5) # don't change reservation
- move_line.lot_id = lot1
- self.assertEqual(move_line.reserved_qty, 5) # don't change reservation when assgning a lot now
- move1._action_done()
- self.assertEqual(move_line.reserved_qty, 0) # change reservation to 0 for done move
- self.assertEqual(move1.state, 'done')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot1, strict=True), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product_lot, self.stock_location)), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product_lot, self.stock_location, lot_id=lot1, strict=True)), 0.0)
- def test_edit_reserved_move_line_8(self):
- """ Send 5 tracked products to a client, but some of these products do not have any lot set
- in our inventory yet: we only set them at delivery time. Adding a lot_id on the move line
- that does not have any should not change its reservation, and validating should not create
- a negative quant for this lot in stock.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- # make some stock without assigning a lot id
- self.env['stock.quant']._update_available_quantity(self.product_lot, self.stock_location, 3)
- self.env['stock.quant']._update_available_quantity(self.product_lot, self.stock_location, 2, lot_id=lot1)
- # creation
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- })
- self.assertEqual(move1.state, 'draft')
- # confirmation
- move1._action_confirm()
- self.assertEqual(move1.state, 'confirmed')
- # assignment
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 2)
- tracked_move_line = None
- untracked_move_line = None
- for move_line in move1.move_line_ids:
- if move_line.lot_id:
- tracked_move_line = move_line
- else:
- untracked_move_line = move_line
- self.assertEqual(tracked_move_line.reserved_qty, 2)
- tracked_move_line.qty_done = 2
- self.assertEqual(untracked_move_line.reserved_qty, 3)
- untracked_move_line.lot_id = lot2
- self.assertEqual(untracked_move_line.reserved_qty, 3) # don't change reservation
- untracked_move_line.qty_done = 3
- self.assertEqual(untracked_move_line.reserved_qty, 3) # don't change reservation
- move1._action_done()
- self.assertEqual(untracked_move_line.reserved_qty, 0) # change reservation to 0 for done move
- self.assertEqual(tracked_move_line.reserved_qty, 0) # change reservation to 0 for done move
- self.assertEqual(move1.state, 'done')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot1, strict=True), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot2, strict=True), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product_lot, self.stock_location)), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product_lot, self.stock_location, lot_id=lot1, strict=True)), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product_lot, self.stock_location, lot_id=lot2, strict=True)), 0.0)
- def test_edit_reserved_move_line_9(self):
- """
- When writing on the reserved quantity on the SML, a process tries to
- reserve the quants with that new quantity. If it fails (for instance
- because the written quantity is more than actually available), this
- quantity should be reset to 0.
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0)
- out_move = self.env['stock.move'].create({
- 'name': self.product.name,
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom_qty': 1,
- 'product_uom': self.product.uom_id.id,
- })
- out_move._action_confirm()
- out_move._action_assign()
- # try to manually assign more than available
- out_move.move_line_ids.reserved_uom_qty = 2
- self.assertTrue(out_move.move_line_ids)
- self.assertEqual(out_move.move_line_ids.reserved_uom_qty, 0, "The reserved quantity should be cancelled")
- def test_edit_done_move_line_1(self):
- """ Test that editing a done stock move line linked to an untracked product correctly and
- directly adapts the transfer. In this case, we edit the sublocation where we take the
- product to another sublocation where a product is available.
- """
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, shelf1_location, 1.0)
- self.env['stock.quant']._update_available_quantity(self.product, shelf2_location, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- # move from shelf1
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- # edit once done, we actually moved from shelf2
- move1.move_line_ids.location_id = shelf2_location.id
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- def test_edit_done_move_line_2(self):
- """ Test that editing a done stock move line linked to a tracked product correctly and directly
- adapts the transfer. In this case, we edit the lot to another available one.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot1)
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 1.0)
- move1.move_line_ids.lot_id = lot2.id
- # reserved_availability should always been 0 for done move.
- self.assertEqual(move1.reserved_availability, 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 0.0)
- def test_edit_done_move_line_3(self):
- """ Test that editing a done stock move line linked to a packed product correctly and directly
- adapts the transfer. In this case, we edit the package to another available one.
- """
- package1 = self.env['stock.quant.package'].create({'name': 'test_edit_reserved_move_line_3'})
- package2 = self.env['stock.quant.package'].create({'name': 'test_edit_reserved_move_line_3'})
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, package_id=package1)
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, package_id=package2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package2), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package2), 1.0)
- move1.move_line_ids.package_id = package2.id
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, package_id=package2), 0.0)
- def test_edit_done_move_line_4(self):
- """ Test that editing a done stock move line linked to an owned product correctly and directly
- adapts the transfer. In this case, we edit the owner to another available one.
- """
- owner1 = self.env['res.partner'].create({'name': 'test_edit_reserved_move_line_4_1'})
- owner2 = self.env['res.partner'].create({'name': 'test_edit_reserved_move_line_4_2'})
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, owner_id=owner1)
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, owner_id=owner2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner2), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner2), 1.0)
- move1.move_line_ids.owner_id = owner2.id
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, owner_id=owner2), 0.0)
- def test_edit_done_move_line_5(self):
- """ Test that editing a done stock move line linked to a packed and tracked product correctly
- and directly adapts the transfer. In this case, we edit the lot to another available one
- that is not in a pack.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product.id,
- 'company_id': self.env.company.id,
- })
- package1 = self.env['stock.quant.package'].create({'name': 'test_edit_reserved_move_line_5'})
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot1, package_id=package1)
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1.0, lot_id=lot2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1, package_id=package1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1, package_id=package1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 1.0)
- move_line = move1.move_line_ids[0]
- move_line.write({'package_id': False, 'lot_id': lot2.id})
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot1, package_id=package1), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, lot_id=lot2), 0.0)
- def test_edit_done_move_line_6(self):
- """ Test that editing a done stock move line linked to an untracked product correctly and
- directly adapts the transfer. In this case, we edit the sublocation where we take the
- product to another sublocation where a product is NOT available.
- """
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, shelf1_location, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- move1.move_line_ids.location_id = shelf2_location.id
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location, allow_negative=True), -1.0)
- def test_edit_done_move_line_7(self):
- """ Test that editing a done stock move line linked to an untracked product correctly and
- directly adapts the transfer. In this case, we edit the sublocation where we take the
- product to another sublocation where a product is NOT available because it has been reserved
- by another move.
- """
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- shelf2_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, shelf1_location, 1.0)
- self.env['stock.quant']._update_available_quantity(self.product, shelf2_location, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 1.0)
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- move2 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move2._action_confirm()
- move2._action_assign()
- self.assertEqual(move2.state, 'assigned')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- move1.move_line_ids.location_id = shelf2_location.id
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf2_location), 0.0)
- self.assertEqual(move2.state, 'confirmed')
- def test_edit_done_move_line_8(self):
- """ Test that editing a done stock move line linked to an untracked product correctly and
- directly adapts the transfer. In this case, we increment the quantity done (and we do not
- have more in stock.
- """
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, shelf1_location, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- # move from shelf1
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- self.assertEqual(move1.product_uom_qty, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- # edit once done, we actually moved 2 products
- move1.move_line_ids.qty_done = 2
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location, allow_negative=True), -1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location, allow_negative=True), -1.0)
- self.assertEqual(move1.product_uom_qty, 2.0)
- def test_edit_done_move_line_9(self):
- """ Test that editing a done stock move line linked to an untracked product correctly and
- directly adapts the transfer. In this case, we "cancel" the move by zeroing the qty done.
- """
- shelf1_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': self.stock_location.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, shelf1_location, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- # move from shelf1
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- self.assertEqual(move1.product_uom_qty, 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- # edit once done, we actually moved 2 products
- move1.move_line_ids.qty_done = 0
- self.assertEqual(move1.product_uom_qty, 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, shelf1_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 1.0)
- def test_edit_done_move_line_10(self):
- """ Edit the quantity done for an incoming move shoudld also remove the quant if there
- are no product in stock.
- """
- # move from shelf1
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 10
- move1._action_done()
- quant = self.gather_relevant(self.product, self.stock_location)
- self.assertEqual(len(quant), 1.0)
- # edit once done, we actually moved 2 products
- move1.move_line_ids.qty_done = 0
- quant = self.gather_relevant(self.product, self.stock_location)
- self.assertEqual(len(quant), 0.0)
- self.assertEqual(move1.product_uom_qty, 0.0)
- def test_edit_done_move_line_11(self):
- """ Add a move line and check if the quant is updated
- """
- owner = self.env['res.partner'].create({'name': 'Jean'})
- picking = self.env['stock.picking'].create({
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'partner_id': owner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- # move from shelf1
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'picking_id': picking.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- })
- picking.action_confirm()
- picking.action_assign()
- move1.move_line_ids.qty_done = 10
- picking._action_done()
- self.assertEqual(move1.product_uom_qty, 10.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 10.0)
- self.env['stock.move.line'].create({
- 'picking_id': move1.move_line_ids.picking_id.id,
- 'move_id': move1.move_line_ids.move_id.id,
- 'product_id': move1.move_line_ids.product_id.id,
- 'qty_done': move1.move_line_ids.qty_done,
- 'product_uom_id': move1.product_uom.id,
- 'location_id': move1.move_line_ids.location_id.id,
- 'location_dest_id': move1.move_line_ids.location_dest_id.id,
- })
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 20.0)
- move1.move_line_ids[1].qty_done = 5
- self.assertEqual(move1.product_uom_qty, 15.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 15.0)
- def test_edit_done_move_line_12(self):
- """ Test that editing a done stock move line linked a tracked product correctly and directly
- adapts the transfer. In this case, we edit the lot to another one, but the original move line
- is not in the default product's UOM.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- package1 = self.env['stock.quant.package'].create({'name': 'test_edit_done_move_line_12'})
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1.move_line_ids.lot_id = lot1.id
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot1), 12.0)
- # Change the done quantity from 1 dozen to two dozen
- move1.move_line_ids.qty_done = 2
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot1), 24.0)
- def test_edit_done_move_line_13(self):
- """ Test that editing a done stock move line linked to a packed and tracked product correctly
- and directly adapts the transfer. In this case, we edit the lot to another available one
- that we put in the same pack.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- package1 = self.env['stock.quant.package'].create({'name': 'test_edit_reserved_move_line_5'})
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.qty_done = 1
- move1.move_line_ids.lot_id = lot1.id
- move1.move_line_ids.result_package_id = package1.id
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot1, package_id=package1), 1.0)
- move1.move_line_ids.write({'lot_id': lot2.id})
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location), 1.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot1, package_id=package1), 0.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_lot, self.stock_location, lot_id=lot2, package_id=package1), 1.0)
- def test_edit_done_move_line_14(self):
- """ Test that editing a done stock move line with a different UoM from its stock move correctly
- updates the quant when its qty and/or its UoM is edited. Also check that we don't allow editing
- a done stock move's UoM.
- """
- move1 = self.env['stock.move'].create({
- 'name': 'test_edit_moveline',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 12.0,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.product_uom_id = self.uom_dozen
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 12.0)
- move1.move_line_ids.qty_done = 2
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 24.0)
- self.assertEqual(move1.product_uom_qty, 24.0)
- self.assertEqual(move1.product_qty, 24.0)
- move1.move_line_ids.product_uom_id = self.uom_unit
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 2.0)
- self.assertEqual(move1.product_uom_qty, 2.0)
- self.assertEqual(move1.product_qty, 2.0)
- with self.assertRaises(UserError):
- move1.product_uom = self.uom_dozen
- def test_immediate_validate_1(self):
- """ In a picking with a single available move, clicking on validate without filling any
- quantities should open a wizard asking to process all the reservation (so, the whole move).
- """
- partner = self.env['res.partner'].create({'name': 'Jean'})
- picking = self.env['stock.picking'].create({
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'partner_id': partner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- self.env['stock.move'].create({
- 'name': 'test_immediate_validate_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'picking_id': picking.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- })
- picking.action_confirm()
- picking.action_assign()
- res_dict = picking.button_validate()
- self.assertEqual(res_dict.get('res_model'), 'stock.immediate.transfer')
- wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
- wizard.process()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 10.0)
- def test_immediate_validate_2(self):
- """ In a picking with a single partially available move, clicking on validate without
- filling any quantities should open a wizard asking to process all the reservation (so, only
- a part of the initial demand). Validating this wizard should open another one asking for
- the creation of a backorder. If the backorder is created, it should contain the quantities
- not processed.
- """
- partner = self.env['res.partner'].create({'name': 'Jean'})
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 5.0)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'partner_id': partner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- self.env['stock.move'].create({
- 'name': 'test_immediate_validate_2',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_id': picking.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- })
- picking.action_confirm()
- picking.action_assign()
- # Only 5 products are reserved on the move of 10, click on `button_validate`.
- res_dict = picking.button_validate()
- self.assertEqual(res_dict.get('res_model'), 'stock.immediate.transfer')
- wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save()
- res_dict_for_back_order = wizard.process()
- self.assertEqual(res_dict_for_back_order.get('res_model'), 'stock.backorder.confirmation')
- backorder_wizard = self.env[(res_dict_for_back_order.get('res_model'))].browse(res_dict_for_back_order.get('res_id')).with_context(res_dict_for_back_order['context'])
- # Chose to create a backorder.
- backorder_wizard.process()
- # Only 5 products should be processed on the initial move.
- self.assertEqual(picking.move_ids.state, 'done')
- self.assertEqual(picking.move_ids.quantity_done, 5.0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 0.0)
- # The backoder should contain a move for the other 5 produts.
- backorder = self.env['stock.picking'].search([('backorder_id', '=', picking.id)])
- self.assertEqual(len(backorder), 1.0)
- self.assertEqual(backorder.move_ids.product_uom_qty, 5.0)
- def test_immediate_validate_3(self):
- """ In a picking with two moves, one partially available and one unavailable, clicking
- on validate without filling any quantities should open a wizard asking to process all the
- reservation (so, only a part of one of the moves). Validating this wizard should open
- another one asking for the creation of a backorder. If the backorder is created, it should
- contain the quantities not processed.
- """
- product5 = self.env['product.product'].create({
- 'name': 'Product 5',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_internal').id,
- })
- product1_move = self.env['stock.move'].create({
- 'name': 'product1_move',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'picking_id': picking.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100,
- })
- product5_move = self.env['stock.move'].create({
- 'name': 'product3_move',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'picking_id': picking.id,
- 'product_id': product5.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 100,
- })
- picking.action_confirm()
- picking.action_assign()
- # product1_move should be partially available (1/100), product5_move should be totally
- # unavailable (0/100)
- self.assertEqual(product1_move.state, 'partially_available')
- self.assertEqual(product5_move.state, 'confirmed')
- action = picking.button_validate()
- self.assertEqual(action.get('res_model'), 'stock.immediate.transfer')
- wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
- action = wizard.process()
- self.assertTrue(isinstance(action, dict), 'Should open backorder wizard')
- self.assertEqual(action.get('res_model'), 'stock.backorder.confirmation')
- wizard = self.env[(action.get('res_model'))].browse(action.get('res_id')).with_context(action.get('context'))
- wizard.process()
- backorder = self.env['stock.picking'].search([('backorder_id', '=', picking.id)])
- self.assertEqual(len(backorder), 1.0)
- # The backorder should contain 99 product1 and 100 product5.
- for backorder_move in backorder.move_ids:
- if backorder_move.product_id.id == self.product.id:
- self.assertEqual(backorder_move.product_qty, 99)
- elif backorder_move.product_id.id == product5.id:
- self.assertEqual(backorder_move.product_qty, 100)
- def test_immediate_validate_4(self):
- """ In a picking with a single available tracked by lot move, clicking on validate without
- filling any quantities should pop up the immediate transfer wizard.
- """
- partner = self.env['res.partner'].create({'name': 'Jean'})
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_lot, self.stock_location, 5.0, lot_id=lot1)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'partner_id': partner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- # move from shelf1
- self.env['stock.move'].create({
- 'name': 'test_immediate_validate_4',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_id': picking.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- })
- picking.action_confirm()
- picking.action_assign()
- # No quantities filled, immediate transfer wizard should pop up.
- immediate_trans_wiz_dict = picking.button_validate()
- self.assertEqual(immediate_trans_wiz_dict.get('res_model'), 'stock.immediate.transfer')
- immediate_trans_wiz = Form(self.env[immediate_trans_wiz_dict['res_model']].with_context(immediate_trans_wiz_dict['context'])).save()
- immediate_trans_wiz.process()
- self.assertEqual(picking.move_ids.quantity_done, 5.0)
- # Check move_lines data
- self.assertEqual(len(picking.move_ids.move_line_ids), 1)
- self.assertEqual(picking.move_ids.move_line_ids.lot_id, lot1)
- self.assertEqual(picking.move_ids.move_line_ids.qty_done, 5.0)
- # Check quants data
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0.0)
- self.assertEqual(len(self.gather_relevant(self.product, self.stock_location)), 0.0)
- def _create_picking_test_immediate_validate_5(self, picking_type_id, product_id):
- picking = self.env['stock.picking'].create({
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'picking_type_id': picking_type_id.id,
- })
- self.env['stock.move'].create({
- 'name': 'move1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'picking_id': picking.id,
- 'picking_type_id': picking_type_id.id,
- 'product_id': product_id.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- })
- picking.action_confirm()
- for line in picking.move_line_ids:
- line.qty_done = line.reserved_uom_qty
- return picking
- def test_immediate_validate_5(self):
- """ In a receipt with a single tracked by serial numbers move, clicking on validate without
- filling any quantities nor lot should open an UserError except if the picking type is
- configured to allow otherwise.
- """
- picking_type_id = self.env.ref('stock.picking_type_in')
- product_id = self.product_serial
- self.assertTrue(picking_type_id.use_create_lots or picking_type_id.use_existing_lots)
- self.assertEqual(product_id.tracking, 'serial')
- picking = self._create_picking_test_immediate_validate_5(picking_type_id, product_id)
- # should raise because no serial numbers were specified
- self.assertRaises(UserError, picking.button_validate)
- picking_type_id.use_create_lots = False
- picking_type_id.use_existing_lots = False
- picking = self._create_picking_test_immediate_validate_5(picking_type_id, product_id)
- picking.button_validate()
- self.assertEqual(picking.state, 'done')
- def test_immediate_validate_6(self):
- """ In a receipt picking with two moves, one tracked and one untracked, clicking on
- validate without filling any quantities should displays an UserError as long as no quantity
- done and lot_name is set on the tracked move. Now if the user validates the picking, the
- wizard telling the user all reserved quantities will be processed will NOT be opened. This
- wizard is only opene if no quantities were filled. So validating the picking at this state
- will open another wizard asking for the creation of a backorder. Now, if the user processed
- on the second move more than the reservation, a wizard will ask him to confirm.
- """
- picking_type = self.env.ref('stock.picking_type_in')
- picking_type.use_create_lots = True
- picking_type.use_existing_lots = False
- picking = self.env['stock.picking'].create({
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'picking_type_id': picking_type.id,
- })
- self.env['stock.move'].create({
- 'name': 'product1_move',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'picking_id': picking.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1,
- })
- product3_move = self.env['stock.move'].create({
- 'name': 'product3_move',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'picking_id': picking.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1,
- })
- picking.action_confirm()
- picking.action_assign()
- with self.assertRaises(UserError):
- picking.button_validate()
- product3_move.move_line_ids[0].qty_done = 1
- with self.assertRaises(UserError):
- picking.button_validate()
- product3_move.move_line_ids[0].lot_name = '271828'
- action = picking.button_validate() # should open backorder wizard
- self.assertTrue(isinstance(action, dict), 'Should open backorder wizard')
- self.assertEqual(action.get('res_model'), 'stock.backorder.confirmation')
- def test_immediate_validate_7(self):
- """ In a picking with a single unavailable move, clicking on validate without filling any
- quantities should display an UserError telling the user he cannot process a picking without
- any processed quantity.
- """
- partner = self.env['res.partner'].create({'name': 'Jean'})
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'partner_id': partner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- self.env['stock.move'].create({
- 'name': 'test_immediate_validate_2',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_id': picking.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- })
- picking.action_confirm()
- picking.action_assign()
- scrap = self.env['stock.scrap'].create({
- 'picking_id': picking.id,
- 'product_id': self.product.id,
- 'product_uom_id': self.uom_unit.id,
- 'scrap_qty': 5.0,
- })
- scrap.do_scrap()
- # No products are reserved on the move of 10, click on `button_validate`.
- with self.assertRaises(UserError):
- picking.button_validate()
- def test_immediate_validate_8(self):
- """Validate three receipts at once."""
- partner = self.env['res.partner'].create({'name': 'Pierre'})
- receipt1 = self.env['stock.picking'].create({
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'partner_id': partner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- self.env['stock.move'].create({
- 'name': 'test_immediate_validate_8_1',
- 'location_id': receipt1.location_id.id,
- 'location_dest_id': receipt1.location_dest_id.id,
- 'picking_id': receipt1.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- })
- receipt1.action_confirm()
- receipt2 = self.env['stock.picking'].create({
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'partner_id': partner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- self.env['stock.move'].create({
- 'name': 'test_immediate_validate_8_2',
- 'location_id': receipt2.location_id.id,
- 'location_dest_id': receipt2.location_dest_id.id,
- 'picking_id': receipt2.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- })
- receipt2.action_confirm()
- receipt3 = self.env['stock.picking'].create({
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'partner_id': partner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- self.env['stock.move'].create({
- 'name': 'test_immediate_validate_8_3',
- 'location_id': receipt3.location_id.id,
- 'location_dest_id': receipt3.location_dest_id.id,
- 'picking_id': receipt3.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- })
- receipt3.action_confirm()
- immediate_trans_wiz_dict = (receipt1 + receipt2).button_validate()
- immediate_trans_wiz = Form(self.env[immediate_trans_wiz_dict['res_model']].with_context(immediate_trans_wiz_dict['context'])).save()
- # The different transfers are displayed to the users.
- self.assertTrue(immediate_trans_wiz.show_transfers)
- # All transfers are processed by default
- self.assertEqual(immediate_trans_wiz.immediate_transfer_line_ids.mapped('to_immediate'), [True, True])
- # Only transfer receipt1
- immediate_trans_wiz.immediate_transfer_line_ids.filtered(lambda line: line.picking_id == receipt2).to_immediate = False
- immediate_trans_wiz.process()
- self.assertEqual(receipt1.state, 'done')
- self.assertEqual(receipt2.state, 'assigned')
- # Transfer receipt2 and receipt3.
- immediate_trans_wiz_dict = (receipt3 + receipt2).button_validate()
- immediate_trans_wiz = Form(self.env[immediate_trans_wiz_dict['res_model']].with_context(immediate_trans_wiz_dict['context'])).save()
- immediate_trans_wiz.process()
- self.assertEqual(receipt2.state, 'done')
- self.assertEqual(receipt3.state, 'done')
- def test_immediate_validate_9_tracked_move_with_0_qty_done(self):
- """When trying to validate a picking as an immediate transfer, the done
- quantity of tracked move should be automatically fulfilled if the
- picking type doesn't use new or existing LN/SN."""
- picking_type_receipt = self.env.ref('stock.picking_type_in')
- picking_type_receipt.use_create_lots = False
- picking_type_receipt.use_existing_lots = False
- picking_form = Form(self.env['stock.picking'])
- picking_form.picking_type_id = picking_type_receipt
- with picking_form.move_ids_without_package.new() as move:
- move.product_id = self.product_serial
- move.product_uom_qty = 4
- with picking_form.move_ids_without_package.new() as move:
- move.product_id = self.product_lot
- move.product_uom_qty = 20
- receipt = picking_form.save()
- receipt.action_confirm()
- immediate_wizard = receipt.button_validate()
- immediate_wizard_form = Form(
- self.env[immediate_wizard['res_model']].with_context(immediate_wizard['context'])
- ).save()
- immediate_wizard_form.process()
- self.assertEqual(receipt.state, 'done')
- def test_immediate_validate_10_tracked_move_without_backorder(self):
- """
- Create a picking for a tracked product, validate it as an
- immediate transfer, and ensure that the backorder wizard is
- not triggered when the qty is reserved.
- """
- picking_type_internal = self.env.ref('stock.picking_type_internal')
- picking_type_internal.use_create_lots = True
- picking_type_internal.use_existing_lots = True
- lot = self.env['stock.lot'].create({
- 'name': 'Lot 1',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_lot, self.stock_location, 10, lot_id=lot)
- picking_form = Form(self.env['stock.picking'])
- picking_form.picking_type_id = picking_type_internal
- with picking_form.move_ids_without_package.new() as move:
- move.product_id = self.product_lot
- move.product_uom_qty = 4
- internal_transfer = picking_form.save()
- internal_transfer.action_confirm()
- immediate_wizard = internal_transfer.button_validate()
- immediate_wizard_form = Form(
- self.env[immediate_wizard['res_model']].with_context(immediate_wizard['context'])
- ).save()
- immediate_wizard_form.process()
- self.assertEqual(internal_transfer.state, 'done')
- def test_set_quantity_done_1(self):
- move1 = self.env['stock.move'].create({
- 'name': 'test_set_quantity_done_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_set_quantity_done_2',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- })
- (move1 + move2)._action_confirm()
- (move1 + move2).write({'quantity_done': 1})
- self.assertEqual(move1.quantity_done, 1)
- self.assertEqual(move2.quantity_done, 1)
- def test_initial_demand_1(self):
- """ Check that the initial demand is set to 0 when creating a move by hand, and
- that changing the product on the move do not reset the initial demand.
- """
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- })
- self.assertEqual(move1.state, 'draft')
- self.assertEqual(move1.product_uom_qty, 1)
- move1.product_uom_qty = 100
- move1.product_id = self.product_serial
- move1._onchange_product_id()
- self.assertEqual(move1.product_uom_qty, 100)
- def test_scrap_1(self):
- """ Check the created stock move and the impact on quants when we scrap a
- storable product.
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1)
- scrap_form = Form(self.env['stock.scrap'])
- scrap_form.product_id = self.product
- scrap_form.scrap_qty = 1
- scrap = scrap_form.save()
- scrap.do_scrap()
- self.assertEqual(scrap.state, 'done')
- move = scrap.move_id
- self.assertEqual(move.state, 'done')
- self.assertEqual(move.quantity_done, 1)
- self.assertEqual(move.scrapped, True)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0)
- def test_scrap_2(self):
- """ Check the created stock move and the impact on quants when we scrap a
- consumable product.
- """
- scrap = self.env['stock.scrap'].create({
- 'product_id': self.product_consu.id,
- 'product_uom_id':self.product_consu.uom_id.id,
- 'scrap_qty': 1,
- })
- self.assertEqual(scrap.name, 'New', 'Name should be New in draft state')
- scrap.do_scrap()
- self.assertTrue(scrap.name.startswith('SP/'), 'Sequence should be Changed after do_scrap')
- self.assertEqual(scrap.state, 'done')
- move = scrap.move_id
- self.assertEqual(move.state, 'done')
- self.assertEqual(move.quantity_done, 1)
- self.assertEqual(move.scrapped, True)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product_consu, self.stock_location), 0)
- def test_scrap_3(self):
- """ Scrap the product of a reserved move line. Check that the move line is
- correctly deleted and that the associated stock move is not assigned anymore.
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1)
- move1 = self.env['stock.move'].create({
- 'name': 'test_scrap_3',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(len(move1.move_line_ids), 1)
- scrap = self.env['stock.scrap'].create({
- 'product_id': self.product.id,
- 'product_uom_id':self.product.uom_id.id,
- 'scrap_qty': 1,
- })
- scrap.do_scrap()
- self.assertEqual(move1.state, 'confirmed')
- self.assertEqual(len(move1.move_line_ids), 0)
- def test_scrap_4(self):
- """ Scrap the product of a picking. Then modify the
- done linked stock move and ensure the scrap quantity is also
- updated.
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 10)
- partner = self.env['res.partner'].create({'name': 'Kimberley'})
- picking = self.env['stock.picking'].create({
- 'name': 'A single picking with one move to scrap',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'partner_id': partner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'A move to confirm and scrap its product',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking.id,
- })
- move1._action_confirm()
- self.assertEqual(move1.state, 'assigned')
- scrap = self.env['stock.scrap'].create({
- 'product_id': self.product.id,
- 'product_uom_id': self.product.uom_id.id,
- 'scrap_qty': 5,
- 'picking_id': picking.id,
- })
- scrap.action_validate()
- self.assertEqual(len(picking.move_ids), 2)
- scrapped_move = picking.move_ids.filtered(lambda m: m.state == 'done')
- self.assertTrue(scrapped_move, 'No scrapped move created.')
- self.assertEqual(scrapped_move.scrap_ids.ids, [scrap.id], 'Wrong scrap linked to the move.')
- self.assertEqual(scrap.scrap_qty, 5, 'Scrap quantity has been modified and is not correct anymore.')
- scrapped_move.quantity_done = 8
- self.assertEqual(scrap.scrap_qty, 8, 'Scrap quantity is not updated.')
- def test_scrap_5(self):
- """ Scrap the product of a reserved move line where the product is reserved in another
- unit of measure. Check that the move line is correctly updated after the scrap.
- """
- # 4 units are available in stock
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 4)
- # try to reserve a dozen
- partner = self.env['res.partner'].create({'name': 'Kimberley'})
- picking = self.env['stock.picking'].create({
- 'name': 'A single picking with one move to scrap',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'partner_id': partner.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'A move to confirm and scrap its product',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking.id,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.reserved_availability, 0.33)
- # scrap a unit
- scrap = self.env['stock.scrap'].create({
- 'product_id': self.product.id,
- 'product_uom_id': self.product.uom_id.id,
- 'scrap_qty': 1,
- 'picking_id': picking.id,
- })
- scrap.action_validate()
- self.assertEqual(scrap.state, 'done')
- self.assertEqual(move1.reserved_availability, 0.25)
- def test_scrap_6(self):
- """ Check that scrap correctly handle UoM. """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1)
- scrap = self.env['stock.scrap'].create({
- 'product_id': self.product.id,
- 'product_uom_id': self.uom_dozen.id,
- 'scrap_qty': 1,
- })
- warning_message = scrap.action_validate()
- self.assertEqual(warning_message.get('res_model', 'Wrong Model'), 'stock.warn.insufficient.qty.scrap')
- insufficient_qty_wizard = self.env['stock.warn.insufficient.qty.scrap'].create({
- 'product_id': self.product.id,
- 'location_id': self.stock_location.id,
- 'scrap_id': scrap.id,
- 'quantity': 1,
- 'product_uom_name': self.product.uom_id.name
- })
- insufficient_qty_wizard.action_done()
- self.assertEqual(self.env['stock.quant']._gather(self.product, self.stock_location).quantity, -11)
- def test_scrap_7_sn_warning(self):
- """ Check serial numbers are correctly double checked """
- child_loc1 = self.env['stock.location'].create({
- 'name': "child_location1",
- 'usage': 'internal',
- 'location_id': self.stock_location.id
- })
- child_loc2 = self.env['stock.location'].create({
- 'name': "child_location2",
- 'usage': 'internal',
- 'location_id': self.stock_location.id
- })
- lot1 = self.env['stock.lot'].create({
- 'name': 'serial1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, child_loc1, 1, lot1)
- scrap = self.env['stock.scrap'].create({
- 'product_id': self.product_serial.id,
- 'product_uom_id': self.uom_unit.id,
- 'location_id': child_loc2.id,
- 'lot_id': lot1.id
- })
- warning = False
- warning = scrap._onchange_serial_number()
- self.assertTrue(warning, 'Use of wrong serial number location not detected')
- self.assertEqual(list(warning.keys())[0], 'warning', 'Warning message was not returned')
- self.assertEqual(scrap.location_id, child_loc1, 'Location was not auto-corrected')
- def test_scrap_8(self):
- """
- Suppose a user wants to scrap some products thanks to internal moves.
- This test checks the state of the picking based on few cases
- """
- scrap_location = self.env['stock.location'].search([('company_id', '=', self.env.company.id), ('scrap_location', '=', True)], limit=1)
- internal_operation = self.env['stock.picking.type'].with_context(active_test=False).search([('code', '=', 'internal'), ('company_id', '=', self.env.company.id)], limit=1)
- internal_operation.active = True
- product01 = self.product
- product02 = self.env['product.product'].create({
- 'name': 'SuperProduct',
- 'type': 'product',
- })
- self.env['stock.quant']._update_available_quantity(product01, self.stock_location, 3)
- self.env['stock.quant']._update_available_quantity(product02, self.stock_location, 1)
- scrap_picking01, scrap_picking02, scrap_picking03 = self.env['stock.picking'].create([{
- 'location_id': self.stock_location.id,
- 'location_dest_id': scrap_location.id,
- 'picking_type_id': internal_operation.id,
- 'move_ids': [(0, 0, {
- 'name': 'Scrap %s' % product.display_name,
- 'location_id': self.stock_location.id,
- 'location_dest_id': scrap_location.id,
- 'product_id': product.id,
- 'product_uom': product.uom_id.id,
- 'product_uom_qty': 1.0,
- 'picking_type_id': internal_operation.id,
- }) for product in products],
- } for products in [(product01,), (product01,), (product01, product02)]])
- (scrap_picking01 + scrap_picking02 + scrap_picking03).action_confirm()
- # All SM are processed
- scrap_picking01.move_ids.quantity_done = 1
- scrap_picking01.button_validate()
- # All SM are cancelled
- scrap_picking02.action_cancel()
- # Process one SM and cancel the other one
- pick03_prod01_move = scrap_picking03.move_ids.filtered(lambda sm: sm.product_id == product01)
- pick03_prod02_move = scrap_picking03.move_ids - pick03_prod01_move
- pick03_prod01_move.quantity_done = 1
- pick03_prod02_move._action_cancel()
- scrap_picking03.button_validate()
- self.assertEqual(scrap_picking01.move_ids.state, 'done')
- self.assertEqual(scrap_picking01.state, 'done')
- self.assertEqual(scrap_picking02.move_ids.state, 'cancel')
- self.assertEqual(scrap_picking02.state, 'cancel')
- self.assertEqual(pick03_prod01_move.state, 'done')
- self.assertEqual(pick03_prod02_move.state, 'cancel')
- self.assertEqual(scrap_picking03.state, 'done')
- self.assertEqual(self.env['stock.quant']._get_available_quantity(product01, self.stock_location), 1)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(product02, self.stock_location), 1)
- def test_in_date_1(self):
- """ Check that moving a tracked quant keeps the incoming date.
- """
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_date_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.lot_name = 'lot1'
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- quant = self.gather_relevant(self.product_lot, self.stock_location)
- self.assertEqual(len(quant), 1.0)
- self.assertNotEqual(quant.in_date, False)
- # Keep a reference to the initial incoming date in order to compare it later.
- initial_incoming_date = quant.in_date
- move2 = self.env['stock.move'].create({
- 'name': 'test_in_date_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move2._action_confirm()
- move2._action_assign()
- move2.move_line_ids.qty_done = 1
- move2._action_done()
- quant = self.gather_relevant(self.product_lot, self.pack_location)
- self.assertEqual(len(quant), 1.0)
- self.assertEqual(quant.in_date, initial_incoming_date)
- def test_in_date_2(self):
- """ Check that editing a done move line for a tracked product and changing its lot
- correctly restores the original lot with its incoming date and remove the new lot
- with its incoming date.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- # receive lot1
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_date_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.lot_id = lot1
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- # receive lot2
- move2 = self.env['stock.move'].create({
- 'name': 'test_in_date_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- move2._action_confirm()
- move2._action_assign()
- move2.move_line_ids.lot_id = lot2
- move2.move_line_ids.qty_done = 1
- move2._action_done()
- initial_in_date_lot2 = self.env['stock.quant'].search([
- ('location_id', '=', self.stock_location.id),
- ('product_id', '=', self.product_lot.id),
- ('lot_id', '=', lot2.id),
- ]).in_date
- # Edit lot1's incoming date.
- quant_lot1 = self.env['stock.quant'].search([
- ('location_id', '=', self.stock_location.id),
- ('product_id', '=', self.product_lot.id),
- ('lot_id', '=', lot1.id),
- ])
- from odoo.fields import Datetime
- from datetime import timedelta
- initial_in_date_lot1 = Datetime.now() - timedelta(days=5)
- quant_lot1.in_date = initial_in_date_lot1
- # Move one quant to pack location
- move3 = self.env['stock.move'].create({
- 'name': 'test_in_date_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move3._action_confirm()
- move3._action_assign()
- move3.move_line_ids.qty_done = 1
- move3._action_done()
- quant_in_pack = self.env['stock.quant'].search([
- ('product_id', '=', self.product_lot.id),
- ('location_id', '=', self.pack_location.id),
- ])
- # As lot1 has an older date and FIFO is set by default, it's the one that should be
- # in pack.
- self.assertEqual(len(quant_in_pack), 1)
- self.assertAlmostEqual(quant_in_pack.in_date, initial_in_date_lot1, delta=timedelta(seconds=1))
- self.assertEqual(quant_in_pack.lot_id, lot1)
- # Now, edit the move line and actually move the other lot
- move3.move_line_ids.lot_id = lot2
- # Check that lot1 correctly is back to stock with its right in_date
- quant_lot1 = self.env['stock.quant'].search([
- ('location_id.usage', '=', 'internal'),
- ('product_id', '=', self.product_lot.id),
- ('lot_id', '=', lot1.id),
- ('quantity', '!=', 0),
- ])
- self.assertEqual(quant_lot1.location_id, self.stock_location)
- self.assertAlmostEqual(quant_lot1.in_date, initial_in_date_lot1, delta=timedelta(seconds=1))
- # Check that lo2 is in pack with is right in_date
- quant_lot2 = self.env['stock.quant'].search([
- ('location_id.usage', '=', 'internal'),
- ('product_id', '=', self.product_lot.id),
- ('lot_id', '=', lot2.id),
- ('quantity', '!=', 0),
- ])
- self.assertEqual(quant_lot2.location_id, self.pack_location)
- self.assertAlmostEqual(quant_lot2.in_date, initial_in_date_lot2, delta=timedelta(seconds=1))
- def test_in_date_3(self):
- """ Check that, when creating a move line on a done stock move, the lot and its incoming
- date are correctly moved to the destination location.
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'lot1',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- lot2 = self.env['stock.lot'].create({
- 'name': 'lot2',
- 'product_id': self.product_lot.id,
- 'company_id': self.env.company.id,
- })
- # receive lot1
- move1 = self.env['stock.move'].create({
- 'name': 'test_in_date_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.move_line_ids.lot_id = lot1
- move1.move_line_ids.qty_done = 1
- move1._action_done()
- # receive lot2
- move2 = self.env['stock.move'].create({
- 'name': 'test_in_date_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- move2._action_confirm()
- move2._action_assign()
- move2.move_line_ids.lot_id = lot2
- move2.move_line_ids.qty_done = 1
- move2._action_done()
- initial_in_date_lot2 = self.env['stock.quant'].search([
- ('location_id', '=', self.stock_location.id),
- ('product_id', '=', self.product_lot.id),
- ('lot_id', '=', lot2.id),
- ('quantity', '!=', 0),
- ]).in_date
- # Edit lot1's incoming date.
- quant_lot1 = self.env['stock.quant'].search([
- ('location_id.usage', '=', 'internal'),
- ('product_id', '=', self.product_lot.id),
- ('lot_id', '=', lot1.id),
- ('quantity', '!=', 0),
- ])
- from odoo.fields import Datetime
- from datetime import timedelta
- initial_in_date_lot1 = Datetime.now() - timedelta(days=5)
- quant_lot1.in_date = initial_in_date_lot1
- # Move one quant to pack location
- move3 = self.env['stock.move'].create({
- 'name': 'test_in_date_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.pack_location.id,
- 'product_id': self.product_lot.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move3._action_confirm()
- move3._action_assign()
- move3.move_line_ids.qty_done = 1
- move3._action_done()
- # Now, also move lot2
- self.env['stock.move.line'].create({
- 'move_id': move3.id,
- 'product_id': move3.product_id.id,
- 'qty_done': 1,
- 'product_uom_id': move3.product_uom.id,
- 'location_id': move3.location_id.id,
- 'location_dest_id': move3.location_dest_id.id,
- 'lot_id': lot2.id,
- })
- quants = self.env['stock.quant'].search([
- ('location_id.usage', '=', 'internal'),
- ('product_id', '=', self.product_lot.id),
- ('quantity', '!=', 0),
- ])
- self.assertEqual(len(quants), 2)
- for quant in quants:
- if quant.lot_id == lot1:
- self.assertAlmostEqual(quant.in_date, initial_in_date_lot1, delta=timedelta(seconds=1))
- elif quant.lot_id == lot2:
- self.assertAlmostEqual(quant.in_date, initial_in_date_lot2, delta=timedelta(seconds=1))
- def test_edit_initial_demand_1(self):
- """ Increase initial demand once everything is reserved and check if
- the existing move_line is updated.
- """
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- move1._action_confirm()
- move1._action_assign()
- move1.product_uom_qty = 15
- # _action_assign is automatically called
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(move1.product_uom_qty, 15)
- self.assertEqual(len(move1.move_line_ids), 1)
- def test_edit_initial_demand_2(self):
- """ Decrease initial demand once everything is reserved and check if
- the existing move_line has been dropped after the updated and another
- is created once the move is reserved.
- """
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- move1._action_confirm()
- move1._action_assign()
- self.assertEqual(move1.state, 'assigned')
- move1.product_uom_qty = 5
- self.assertEqual(move1.state, 'assigned')
- self.assertEqual(move1.product_uom_qty, 5)
- self.assertEqual(len(move1.move_line_ids), 1)
- def test_initial_demand_3(self):
- """ Increase the initial demand on a receipt picking, the system should automatically
- reserve the new quantity.
- """
- picking = self.env['stock.picking'].create({
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- 'immediate_transfer': True,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'quantity_done': 10.0,
- 'picking_id': picking.id,
- })
- picking._autoconfirm_picking()
- self.assertEqual(picking.state, 'assigned')
- move1.quantity_done = 12
- self.assertEqual(picking.state, 'assigned')
- def test_initial_demand_4(self):
- """ Increase the initial demand on a delivery picking, the system should not automatically
- reserve the new quantity.
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 12)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- 'picking_id': picking.id,
- })
- picking.action_confirm()
- picking.action_assign()
- self.assertEqual(picking.state, 'assigned')
- move1.product_uom_qty = 12
- self.assertEqual(picking.state, 'assigned') # actually, partially available
- self.assertEqual(move1.state, 'partially_available')
- picking.action_assign()
- self.assertEqual(move1.state, 'assigned')
- def test_change_product_type(self):
- """ Changing type of an existing product will raise a user error if
- - some move are reserved
- - switching from a stockable product when qty_available is not zero
- - switching the product type when there are already done moves
- """
- move_in = self.env['stock.move'].create({
- 'name': 'test_customer',
- 'location_id': self.customer_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move_in._action_confirm()
- move_in._action_assign()
- # Check raise UserError(_("You can not change the type of a product that is currently reserved on a stock
- with self.assertRaises(UserError):
- self.product.detailed_type = 'consu'
- move_in._action_cancel()
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 10)
- # Check raise UserError(_("Available quantity should be set to zero before changing detailed_type"))
- with self.assertRaises(UserError):
- self.product.detailed_type = 'consu'
- move_out = self.env['stock.move'].create({
- 'name': 'test_customer',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': self.product.qty_available,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move_out._action_confirm()
- move_out._action_assign()
- move_out.quantity_done = self.product.qty_available
- move_out._action_done()
- # Check raise UserError(_("You can not change the type of a product that was already used."))
- with self.assertRaises(UserError):
- self.product.detailed_type = 'consu'
- move2 = self.env['stock.move'].create({
- 'name': 'test_customer',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move2._action_confirm()
- move2._action_assign()
- with self.assertRaises(UserError):
- self.product.detailed_type = 'consu'
- move2._action_cancel()
- with self.assertRaises(UserError):
- self.product.detailed_type = 'consu'
- def test_edit_done_picking_1(self):
- """ Add a new move line in a done picking should generate an
- associated move.
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 12)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_in').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- 'picking_id': picking.id,
- })
- picking.action_confirm()
- picking.action_assign()
- move1.quantity_done = 10
- picking._action_done()
- self.assertEqual(len(picking.move_ids), 1, 'One move should exist for the picking.')
- self.assertEqual(len(picking.move_line_ids), 1, 'One move line should exist for the picking.')
- ml = self.env['stock.move.line'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom_id': self.uom_unit.id,
- 'qty_done': 2.0,
- 'picking_id': picking.id,
- })
- self.assertEqual(len(picking.move_ids), 2, 'The new move associated to the move line does not exist.')
- self.assertEqual(len(picking.move_line_ids), 2, 'It should be 2 move lines for the picking.')
- self.assertTrue(ml.move_id in picking.move_ids, 'Links are not correct between picking, moves and move lines.')
- self.assertEqual(picking.state, 'done', 'Picking should still done after adding a new move line.')
- self.assertTrue(all(move.state == 'done' for move in picking.move_ids), 'Wrong state for move.')
- def test_put_in_pack_1(self):
- """ Check that reserving a move and adding its move lines to
- different packages work as expected.
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 2)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- 'picking_id': picking.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- picking.action_confirm()
- picking.action_assign()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0)
- move1.quantity_done = 1
- picking.action_put_in_pack()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0)
- self.assertEqual(len(picking.move_line_ids), 2)
- unpacked_ml = picking.move_line_ids.filtered(lambda ml: not ml.result_package_id)
- self.assertEqual(unpacked_ml.reserved_qty, 1)
- unpacked_ml.qty_done = 1
- picking.action_put_in_pack()
- self.assertEqual(len(picking.move_line_ids), 2)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0)
- picking.button_validate()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.customer_location), 2)
- def test_put_in_pack_2(self):
- """Check that reserving moves without done quantity
- adding in same package.
- """
- product1 = self.env['product.product'].create({
- 'name': 'Product B',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1)
- self.env['stock.quant']._update_available_quantity(product1, self.stock_location, 2)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking.id,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_transit_2',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': product1.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- 'picking_id': picking.id,
- })
- picking.action_confirm()
- picking.action_assign()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, self.stock_location), 0)
- picking.action_put_in_pack()
- self.assertEqual(len(picking.move_line_ids), 2)
- self.assertEqual(picking.move_line_ids[0].qty_done, 1, "Stock move line should have 1 quantity as a done quantity.")
- self.assertEqual(picking.move_line_ids[1].qty_done, 2, "Stock move line should have 2 quantity as a done quantity.")
- line1_result_package = picking.move_line_ids[0].result_package_id
- line2_result_package = picking.move_line_ids[1].result_package_id
- self.assertEqual(line1_result_package, line2_result_package, "Product and Product1 should be in a same package.")
- def test_put_in_pack_3(self):
- """Check that one reserving move without done quantity and
- another reserving move with done quantity adding in different
- package.
- """
- product1 = self.env['product.product'].create({
- 'name': 'Product B',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 1)
- self.env['stock.quant']._update_available_quantity(product1, self.stock_location, 2)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- 'picking_id': picking.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_transit_2',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': product1.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- 'picking_id': picking.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- picking.action_confirm()
- picking.action_assign()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 0)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(product1, self.stock_location), 0)
- move1.quantity_done = 1
- picking.action_put_in_pack()
- move2.quantity_done = 2
- picking.action_put_in_pack()
- self.assertEqual(len(picking.move_line_ids), 2)
- line1_result_package = picking.move_line_ids[0].result_package_id
- line2_result_package = picking.move_line_ids[1].result_package_id
- self.assertNotEqual(line1_result_package, line2_result_package, "Product and Product1 should be in a different package.")
- def test_move_line_aggregated_product_quantities(self):
- """ Test the `stock.move.line` method `_get_aggregated_product_quantities`,
- who returns data used to print delivery slips.
- """
- # Creates two other products.
- product2 = self.env['product.product'].create({
- 'name': 'Product B',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- product3 = self.env['product.product'].create({
- 'name': 'Product C',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- # Adds some quantity on stock.
- self.env['stock.quant'].with_context(inventory_mode=True).create([{
- 'product_id': self.product.id,
- 'inventory_quantity': 100,
- 'location_id': self.stock_location.id,
- }, {
- 'product_id': product2.id,
- 'inventory_quantity': 100,
- 'location_id': self.stock_location.id,
- }, {
- 'product_id': product3.id,
- 'inventory_quantity': 100,
- 'location_id': self.stock_location.id,
- }]).action_apply_inventory()
- # Creates a delivery for a bunch of products.
- delivery_form = Form(self.env['stock.picking'])
- delivery_form.picking_type_id = self.env.ref('stock.picking_type_out')
- with delivery_form.move_ids_without_package.new() as move:
- move.product_id = self.product
- move.product_uom_qty = 10
- with delivery_form.move_ids_without_package.new() as move:
- move.product_id = product2
- move.product_uom_qty = 10
- with delivery_form.move_ids_without_package.new() as move:
- move.product_id = product3
- move.product_uom_qty = 10
- delivery = delivery_form.save()
- delivery.action_confirm()
- # Delivers a part of the quantity, creates a backorder for the remaining qty.
- delivery.move_line_ids.filtered(lambda ml: ml.product_id == self.product).qty_done = 6
- delivery.move_line_ids.filtered(lambda ml: ml.product_id == product2).qty_done = 2
- backorder_wizard_dict = delivery.button_validate()
- backorder_wizard_form = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context']))
- backorder_wizard_form.save().process() # Creates the backorder.
- first_backorder = self.env['stock.picking'].search([('backorder_id', '=', delivery.id)], limit=1)
- # Checks the values.
- aggregate_values = delivery.move_line_ids._get_aggregated_product_quantities()
- self.assertEqual(len(aggregate_values), 2)
- sml1 = delivery.move_line_ids.filtered(lambda ml: ml.product_id == self.product)
- sml2 = delivery.move_line_ids.filtered(lambda ml: ml.product_id == product2)
- aggregate_val_1 = aggregate_values[f'{self.product.id}_{self.product.name}__{sml1.product_uom_id.id}']
- aggregate_val_2 = aggregate_values[f'{product2.id}_{product2.name}__{sml2.product_uom_id.id}']
- self.assertEqual(aggregate_val_1['qty_ordered'], 10)
- self.assertEqual(aggregate_val_1['qty_done'], 6)
- self.assertEqual(aggregate_val_2['qty_ordered'], 10)
- self.assertEqual(aggregate_val_2['qty_done'], 2)
- # Delivers a part of the BO's qty., and creates an another backorder.
- first_backorder.move_line_ids.filtered(lambda ml: ml.product_id == self.product).qty_done = 4
- first_backorder.move_line_ids.filtered(lambda ml: ml.product_id == product2).qty_done = 6
- first_backorder.move_line_ids.filtered(lambda ml: ml.product_id == product3).qty_done = 7
- backorder_wizard_dict = first_backorder.button_validate()
- backorder_wizard_form = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context']))
- backorder_wizard_form.save().process() # Creates the backorder.
- second_backorder = self.env['stock.picking'].search([('backorder_id', '=', first_backorder.id)], limit=1)
- # Checks the values for the original delivery.
- aggregate_values = delivery.move_line_ids._get_aggregated_product_quantities()
- self.assertEqual(len(aggregate_values), 2)
- sml1 = delivery.move_line_ids.filtered(lambda ml: ml.product_id == self.product)
- sml2 = delivery.move_line_ids.filtered(lambda ml: ml.product_id == product2)
- aggregate_val_1 = aggregate_values[f'{self.product.id}_{self.product.name}__{sml1.product_uom_id.id}']
- aggregate_val_2 = aggregate_values[f'{product2.id}_{product2.name}__{sml2.product_uom_id.id}']
- self.assertEqual(aggregate_val_1['qty_ordered'], 10)
- self.assertEqual(aggregate_val_1['qty_done'], 6)
- self.assertEqual(aggregate_val_2['qty_ordered'], 10)
- self.assertEqual(aggregate_val_2['qty_done'], 2)
- # Checks the values for the first back order.
- aggregate_values = first_backorder.move_line_ids._get_aggregated_product_quantities()
- self.assertEqual(len(aggregate_values), 3)
- sml1 = first_backorder.move_line_ids.filtered(lambda ml: ml.product_id == self.product)
- sml2 = first_backorder.move_line_ids.filtered(lambda ml: ml.product_id == product2)
- sml3 = first_backorder.move_line_ids.filtered(lambda ml: ml.product_id == product3)
- aggregate_val_1 = aggregate_values[f'{self.product.id}_{self.product.name}__{sml1.product_uom_id.id}']
- aggregate_val_2 = aggregate_values[f'{product2.id}_{product2.name}__{sml2.product_uom_id.id}']
- aggregate_val_3 = aggregate_values[f'{product3.id}_{product3.name}__{sml3.product_uom_id.id}']
- self.assertEqual(aggregate_val_1['qty_ordered'], 4)
- self.assertEqual(aggregate_val_1['qty_done'], 4)
- self.assertEqual(aggregate_val_2['qty_ordered'], 8)
- self.assertEqual(aggregate_val_2['qty_done'], 6)
- self.assertEqual(aggregate_val_3['qty_ordered'], 10)
- self.assertEqual(aggregate_val_3['qty_done'], 7)
- # Delivers a part of the second BO's qty. but doesn't create a backorder this time.
- second_backorder.move_line_ids.filtered(lambda ml: ml.product_id == product3).qty_done = 3
- backorder_wizard_dict = second_backorder.button_validate()
- backorder_wizard_form = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context']))
- backorder_wizard_form.save().process_cancel_backorder()
- # Checks again the values for the original delivery.
- aggregate_values = delivery.move_line_ids._get_aggregated_product_quantities()
- self.assertEqual(len(aggregate_values), 2)
- sml1 = delivery.move_line_ids.filtered(lambda ml: ml.product_id == self.product)
- sml2 = delivery.move_line_ids.filtered(lambda ml: ml.product_id == product2)
- aggregate_val_1 = aggregate_values[f'{self.product.id}_{self.product.name}__{sml1.product_uom_id.id}']
- aggregate_val_2 = aggregate_values[f'{product2.id}_{product2.name}__{sml2.product_uom_id.id}']
- self.assertEqual(aggregate_val_1['qty_ordered'], 10)
- self.assertEqual(aggregate_val_1['qty_done'], 6)
- self.assertEqual(aggregate_val_2['qty_ordered'], 10)
- self.assertEqual(aggregate_val_2['qty_done'], 2)
- # Checks again the values for the first back order.
- aggregate_values = first_backorder.move_line_ids._get_aggregated_product_quantities()
- self.assertEqual(len(aggregate_values), 3)
- sml1 = first_backorder.move_line_ids.filtered(lambda ml: ml.product_id == self.product)
- sml2 = first_backorder.move_line_ids.filtered(lambda ml: ml.product_id == product2)
- sml3 = first_backorder.move_line_ids.filtered(lambda ml: ml.product_id == product3)
- aggregate_val_1 = aggregate_values[f'{self.product.id}_{self.product.name}__{sml1.product_uom_id.id}']
- aggregate_val_2 = aggregate_values[f'{product2.id}_{product2.name}__{sml2.product_uom_id.id}']
- aggregate_val_3 = aggregate_values[f'{product3.id}_{product3.name}__{sml3.product_uom_id.id}']
- self.assertEqual(aggregate_val_1['qty_ordered'], 4)
- self.assertEqual(aggregate_val_1['qty_done'], 4)
- self.assertEqual(aggregate_val_2['qty_ordered'], 8)
- self.assertEqual(aggregate_val_2['qty_done'], 6)
- self.assertEqual(aggregate_val_3['qty_ordered'], 10)
- self.assertEqual(aggregate_val_3['qty_done'], 7)
- # Checks the values for the second back order.
- aggregate_values = second_backorder.move_line_ids._get_aggregated_product_quantities()
- self.assertEqual(len(aggregate_values), 2)
- sml1 = second_backorder.move_line_ids.filtered(lambda ml: ml.product_id == product3)
- sm2 = second_backorder.move_ids.filtered(lambda ml: ml.product_id == product2)
- aggregate_val_1 = aggregate_values[f'{product3.id}_{product3.name}__{sml1.product_uom_id.id}']
- aggregate_val_2 = aggregate_values[f'{product2.id}_{product2.name}__{sm2.product_uom.id}']
- self.assertEqual(aggregate_val_1['qty_ordered'], 3)
- self.assertEqual(aggregate_val_1['qty_done'], 3)
- self.assertEqual(aggregate_val_2['qty_ordered'], 2)
- self.assertEqual(aggregate_val_2['qty_done'], 0)
- def test_move_line_aggregated_product_quantities_duplicate_stock_move(self):
- """ Test the `stock.move.line` method `_get_aggregated_product_quantities`,
- which returns data used to print delivery slips, with two stock moves of the same product
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 25)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 10.0,
- 'picking_id': picking.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move2 = self.env['stock.move'].create({
- 'name': 'test_transit_2',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 5.0,
- 'picking_id': picking.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- self.env['stock.move.line'].create({
- 'move_id': move1.id,
- 'product_id': move1.product_id.id,
- 'qty_done': 10,
- 'product_uom_id': move1.product_uom.id,
- 'picking_id': picking.id,
- 'location_id': move1.location_id.id,
- 'location_dest_id': move1.location_dest_id.id,
- })
- self.env['stock.move.line'].create({
- 'move_id': move2.id,
- 'product_id': move2.product_id.id,
- 'qty_done': 5,
- 'product_uom_id': move2.product_uom.id,
- 'picking_id': picking.id,
- 'location_id': move2.location_id.id,
- 'location_dest_id': move2.location_dest_id.id,
- })
- aggregate_values = picking.move_line_ids._get_aggregated_product_quantities()
- aggregated_val = aggregate_values[f'{self.product.id}_{self.product.name}__{self.product.uom_id.id}']
- self.assertEqual(aggregated_val['qty_ordered'], 15)
- picking.button_validate()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 10)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.customer_location), 15)
- def test_move_line_aggregated_product_quantities_two_packages(self):
- """ Test the `stock.move.line` method `_get_aggregated_product_quantities`,
- which returns data used to print delivery slips, with two packages
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 25)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 15.0,
- 'picking_id': picking.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- picking.action_confirm()
- picking.action_assign()
- move1.quantity_done = 5
- picking.action_put_in_pack() # Create a first package
- self.assertEqual(len(picking.move_line_ids), 2)
- unpacked_ml = picking.move_line_ids.filtered(lambda ml: not ml.result_package_id)
- self.assertEqual(unpacked_ml.reserved_qty, 10)
- unpacked_ml.qty_done = 10
- picking.action_put_in_pack() # Create a second package
- self.assertEqual(len(picking.move_line_ids), 2)
- picking.button_validate()
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 10)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.customer_location), 15)
- aggregate_values1 = picking.move_line_ids[0]._get_aggregated_product_quantities(strict=True)
- aggregated_val = aggregate_values1[f'{self.product.id}_{self.product.name}__{self.product.uom_id.id}']
- self.assertEqual(aggregated_val['qty_ordered'], 10)
- aggregate_values2 = picking.move_line_ids[1]._get_aggregated_product_quantities(strict=True)
- aggregated_val = aggregate_values2[f'{self.product.id}_{self.product.name}__{self.product.uom_id.id}']
- self.assertEqual(aggregated_val['qty_ordered'], 5)
- def test_move_line_aggregated_product_quantities_incomplete_package(self):
- """ Test the `stock.move.line` method `_get_aggregated_product_quantities`,
- which returns data used to print delivery slips, with an incomplete order put in packages
- """
- self.env['stock.quant']._update_available_quantity(self.product, self.stock_location, 25)
- picking = self.env['stock.picking'].create({
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move1 = self.env['stock.move'].create({
- 'name': 'test_transit_1',
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 15.0,
- 'picking_id': picking.id,
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- })
- move1.quantity_done = 5
- picking.action_put_in_pack() # Create a package
- delivery_form = Form(picking)
- delivery = delivery_form.save()
- delivery.action_confirm()
- backorder_wizard_dict = delivery.button_validate()
- backorder_wizard_form = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context']))
- backorder_wizard_form.save().process_cancel_backorder() # Don't create a backorder
- aggregate_values = picking.move_line_ids._get_aggregated_product_quantities()
- aggregated_val = aggregate_values[f'{self.product.id}_{self.product.name}__{self.product.uom_id.id}']
- self.assertEqual(aggregated_val['qty_ordered'], 15)
- self.assertEqual(aggregated_val['qty_done'], 5)
- aggregate_values = picking.move_line_ids._get_aggregated_product_quantities(strict=True)
- aggregated_val = aggregate_values[f'{self.product.id}_{self.product.name}__{self.product.uom_id.id}']
- self.assertEqual(aggregated_val['qty_ordered'], 5)
- self.assertEqual(aggregated_val['qty_done'], 5)
- aggregate_values = picking.move_line_ids._get_aggregated_product_quantities(except_package=True)
- aggregated_val = aggregate_values[f'{self.product.id}_{self.product.name}__{self.product.uom_id.id}']
- self.assertEqual(aggregated_val['qty_ordered'], 10)
- self.assertEqual(aggregated_val['qty_done'], False)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.stock_location), 20)
- self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product, self.customer_location), 5)
- def test_move_sn_warning(self):
- """ Check that warnings pop up when duplicate SNs added or when SN isn't in
- expected location.
- Two cases covered:
- - Check for dupes when assigning serial number to a stock move
- - Check for dupes when assigning serial number to a stock move line
- """
- lot1 = self.env['stock.lot'].create({
- 'name': 'serial1',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- self.env['stock.quant']._update_available_quantity(self.product_serial, self.pack_location, 1, lot1)
- move = self.env['stock.move'].create({
- 'name': 'test sn',
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product_serial.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move_line = self.env['stock.move.line'].create({
- 'move_id': move.id,
- 'product_id': move.product_id.id,
- 'qty_done': 1,
- 'product_uom_id': move.product_uom.id,
- 'location_id': move.location_id.id,
- 'location_dest_id': move.location_dest_id.id,
- 'lot_name': lot1.name,
- })
- warning = False
- warning = move_line._onchange_serial_number()
- self.assertTrue(warning, 'Reuse of existing serial number (name) not detected')
- self.assertEqual(list(warning.keys())[0], 'warning', 'Warning message was not returned')
- move_line.write({
- 'lot_name': False,
- 'lot_id': lot1.id
- })
- warning = False
- warning = move_line._onchange_serial_number()
- self.assertTrue(warning, 'Reuse of existing serial number (record) not detected')
- self.assertEqual(list(warning.keys())[0], 'warning', 'Warning message was not returned')
- self.assertEqual(move_line.location_id, self.pack_location, 'Location was not auto-corrected')
- move.lot_ids = lot1
- warning = False
- warning = move._onchange_lot_ids()
- self.assertTrue(warning, 'Reuse of existing serial number (record) not detected')
- self.assertEqual(list(warning.keys())[0], 'warning', 'Warning message was not returned')
- def test_forecast_availability(self):
- """ Make an outgoing picking in dozens for a product stored in units.
- Check that reserved_availabity is expressed in move uom and forecast_availability is in product base uom
- """
- # create product
- product = self.env['product.product'].create({
- 'name': 'Product In Units',
- 'type': 'product',
- 'categ_id': self.env.ref('product.product_category_all').id,
- })
- # make some stock
- self.env['stock.quant']._update_available_quantity(product, self.stock_location, 36.0)
- # create picking
- picking_out = self.env['stock.picking'].create({
- 'picking_type_id': self.env.ref('stock.picking_type_out').id,
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id})
- move = self.env['stock.move'].create({
- 'name': product.name,
- 'product_id': product.id,
- 'product_uom': self.uom_dozen.id,
- 'product_uom_qty': 2.0,
- 'picking_id': picking_out.id,
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id})
- # confirm
- picking_out.action_confirm()
- # check availability
- picking_out.action_assign()
- # check reserved_availabity expressed in move uom
- self.assertEqual(move.reserved_availability, 2)
- # check forecast_availability expressed in product base uom
- self.assertEqual(move.forecast_availability, 24)
- def test_SML_location_selection(self):
- """
- Suppose the setting 'Storage Categories' disabled and the option 'Show Detailed Operations'
- for operation 'Internal Transfer' enabled.
- A user creates an internal transfer from F to T, confirms it then adds a SML and selects
- another destination location L (with L a child of T). When the user completes the field
- `qty_done`, the onchange should n't change the destination location L
- """
- self.env.user.write({'groups_id': [(3, self.env.ref('stock.group_stock_storage_categories').id)]})
- internal_transfer = self.env.ref('stock.picking_type_internal')
- internal_transfer.show_operations = True
- picking = self.env['stock.picking'].create({
- 'picking_type_id': internal_transfer.id,
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- })
- self.env['stock.move'].create({
- 'name': self.product_consu.name,
- 'product_id': self.product_consu.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 2.0,
- 'picking_id': picking.id,
- 'location_id': picking.location_id.id,
- 'location_dest_id': picking.location_dest_id.id,
- })
- picking.action_confirm()
- with Form(picking) as form:
- with form.move_line_ids_without_package.edit(0) as line:
- line.location_dest_id = self.stock_location.child_ids[0]
- line.qty_done = 1
- self.assertEqual(picking.move_line_ids_without_package.location_dest_id, self.stock_location.child_ids[0])
- def test_inter_wh_and_forecast_availability(self):
- dest_wh = self.env['stock.warehouse'].create({
- 'name': 'Second Warehouse',
- 'code': 'WH02',
- })
- move = self.env['stock.move'].create({
- 'name': 'test_interwh',
- 'location_id': self.stock_location.id,
- 'location_dest_id': dest_wh.lot_stock_id.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- self.assertEqual(move.forecast_availability, -1)
- move._action_confirm()
- self.assertEqual(move.forecast_availability, -1)
- def test_move_compute_uom(self):
- move = self.env['stock.move'].create({
- 'name': 'foo',
- 'product_id': self.product.id,
- 'location_id': self.stock_location.id,
- 'location_dest_id': self.customer_location.id,
- 'move_line_ids': [(0, 0, {})]
- })
- self.assertEqual(move.product_uom, self.product.uom_id)
- self.assertEqual(move.move_line_ids.product_uom_id, self.product.uom_id)
- uom_kg = self.env.ref('uom.product_uom_kgm')
- product1 = self.env['product.product'].create({
- 'name': 'product1',
- 'type': 'product',
- 'uom_id': uom_kg.id,
- 'uom_po_id': uom_kg.id
- })
- move.product_id = product1
- self.assertEqual(move.product_uom, product1.uom_id)
- def test_move_line_compute_locations(self):
- stock_location = self.env['stock.location'].create({
- 'name': 'test-stock',
- 'usage': 'internal',
- })
- shelf_location = self.env['stock.location'].create({
- 'name': 'shelf1',
- 'usage': 'internal',
- 'location_id': stock_location.id,
- })
- move = self.env['stock.move'].create({
- 'name': 'foo',
- 'product_id': self.product.id,
- 'location_id': stock_location.id,
- 'location_dest_id': shelf_location.id,
- 'move_line_ids': [(0, 0, {})]
- })
- self.assertEqual(move.move_line_ids.location_id, stock_location)
- self.assertEqual(move.move_line_ids.location_dest_id, shelf_location)
- # directly created mls should default to picking's src/dest locations
- internal_transfer = self.env.ref('stock.picking_type_internal')
- picking = self.env['stock.picking'].create({
- 'picking_type_id': internal_transfer.id,
- 'location_id': stock_location.id,
- 'location_dest_id': shelf_location.id,
- 'move_line_nosuggest_ids': [Command.create({
- 'product_id': self.product.id,
- 'qty_done': 1.0
- })]
- })
- self.assertEqual(picking.move_line_ids.location_id.id, stock_location.id)
- self.assertEqual(picking.move_line_ids.location_dest_id.id, shelf_location.id)
- def test_receive_more_and_in_child_location(self):
- """
- Ensure that, when receiving more than expected, and when the destination
- location of the SML is different from the SM one, the SM validation will
- not change the destination location of the SML
- """
- move = self.env['stock.move'].create({
- 'name': self.product.name,
- 'location_id': self.supplier_location.id,
- 'location_dest_id': self.stock_location.id,
- 'product_id': self.product.id,
- 'product_uom': self.uom_unit.id,
- 'product_uom_qty': 1.0,
- })
- move._action_confirm()
- move.move_line_ids.write({
- 'location_dest_id': self.stock_location.child_ids[0].id,
- 'qty_done': 3,
- })
- move._action_done()
- self.assertEqual(move.move_line_ids.qty_done, 3)
- self.assertEqual(move.move_line_ids.location_dest_id, self.stock_location.child_ids[0])
- def test_serial_tracking(self):
- """
- Since updating the move's `lot_ids` field for product tracked by serial numbers will
- also updates the move's `quantity_done`, this test checks the move's move lines will be
- correclty updated and consequently its picking can be validated.
- """
- sn = self.env['stock.lot'].create({
- 'name': 'test_lot_001',
- 'product_id': self.product_serial.id,
- 'company_id': self.env.company.id,
- })
- picking_form = Form(self.env['stock.picking'])
- picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
- with picking_form.move_ids_without_package.new() as move:
- move.product_id = self.product_serial
- move.product_uom_qty = 1
- receipt = picking_form.save()
- receipt.action_confirm()
- receipt_form = Form(receipt)
- with receipt_form.move_ids_without_package.edit(0) as move:
- move.lot_ids.add(sn)
- receipt = receipt_form.save()
- receipt.button_validate()
- self.assertEqual(receipt.state, 'done')
- self.assertEqual(len(receipt.move_line_ids), 1)
- self.assertEqual(receipt.move_line_ids.qty_done, 1)
|