NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

importExportUI.py (24989B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\planet\importExportUI.py
      2 import carbonui.const as uiconst
      3 import blue
      4 import utillib
      5 from carbonui.control.combo import Combo
      6 from carbonui.primitives.container import Container
      7 from carbonui.primitives.containerAutoSize import ContainerAutoSize
      8 from carbonui.primitives.fill import Fill
      9 from carbonui.button.group import ButtonGroup
     10 from eve.client.script.ui.control.entries.item import Item
     11 from eve.client.script.ui.control.entries.util import GetFromClass
     12 from carbonui.control.window import Window
     13 from eve.client.script.ui.control.eveLabel import EveHeaderMedium
     14 from eve.client.script.ui.control.themeColored import SpriteThemeColored
     15 from eve.client.script.ui.util import uix
     16 import localization
     17 import log
     18 import eve.common.script.util.planetCommon as planetCommon
     19 from eve.client.script.ui.inflight.orbitalConfiguration import OrbitalConfigurationWindow
     20 from eve.client.script.ui.shared.planet import planetCommon as planetCommonUI
     21 from carbonui.uicore import uicore
     22 from eve.client.script.ui.control import eveLabel, eveScroll
     23 from eve.client.script.ui.control.gauge import Gauge
     24 from eve.common.lib import appConst as const
     25 from eve.common.script.net import eveMoniker
     26 from eve.common.script.util.eveFormat import FmtISK
     27 from eveexceptions import UserError
     28 from inventorycommon.util import GetTypeVolume
     29 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     30 GAUGE_WIDTH = 180
     31 
     32 class CustomsItem(Item):
     33     __guid__ = 'listentry.CustomsItem'
     34     isDragObject = True
     35 
     36     def Startup(self, *args):
     37         Item.Startup(self, *args)
     38         self.sr.selectedEntry = Fill(parent=self, padTop=1, padBottom=1, color=(0.0, 1.0, 0.0, 0.25))
     39         self.sr.selectedEntry.state = uiconst.UI_HIDDEN
     40         if self.sr.node.isItemTransfer:
     41             self.sr.selectedEntry.state = uiconst.UI_PICKCHILDREN
     42 
     43     def GetDragData(self, *args):
     44         ret = []
     45         for node in self.sr.node.scroll.GetSelectedNodes(self.sr.node):
     46             if node.item is not None:
     47                 item = uix.GetItemData(node.item, 'icon')
     48                 item.scroll = node.scroll
     49                 item.itemID = node.itemID
     50                 item.typeID = node.typeID
     51                 item.quantity = node.quantity
     52                 ret.append(item)
     53             else:
     54                 ret.append(node)
     55 
     56         return ret
     57 
     58     def LoadTooltipPanel(self, tooltipPanel, *args):
     59         planetCommonUI.LoadProductTooltipPanel(tooltipPanel, self.sr.node.typeID)
     60 
     61 
     62 class PlanetaryImportExportUI(Window):
     63     default_windowID = 'PlanetaryImportExportUI'
     64     default_iconNum = 'res:/ui/Texture/WindowIcons/items.png'
     65     default_minSize = (560, 400)
     66     default_isStackable = False
     67     default_scope = uiconst.SCOPE_INFLIGHT
     68     __notifyevents__ = ['OnItemChange',
     69      'OnPlanetPinsChanged',
     70      'OnBallparkCall',
     71      'OnRefreshPins',
     72      'OnPlanetChangesSubmitted',
     73      'OnSkyhookFirstActivation']
     74 
     75     def ApplyAttributes(self, attributes):
     76         Window.ApplyAttributes(self, attributes)
     77         self.spaceportPinID = attributes.spaceportPinID
     78         self.customsOfficeID = attributes.customsOfficeID
     79         self.id = self.customsOfficeID
     80         self.customsOffice = sm.GetService('invCache').GetInventoryFromId(self.customsOfficeID)
     81         if not self.customsOffice:
     82             raise RuntimeError('PlanetaryImportExportUI::Cannot find cargo link with ID %s' % str(self.customsOfficeID))
     83         bp = sm.GetService('michelle').GetBallpark()
     84         if self.customsOfficeID not in bp.slimItems:
     85             raise RuntimeError('OpenPlanetCustomsOfficeImportWindow::Failed to get cargo link data for link ID %s' % str(self.customsOfficeID))
     86         self.customsOfficeItem = bp.slimItems[self.customsOfficeID]
     87         self.customsOfficeLevel = self.customsOfficeItem.level
     88         if self.customsOfficeItem.planetID is None:
     89             raise RuntimeError('OpenPlanetCustomsOfficeImportWindow::Customs office slim item has no planetID set, most likely failed to startup correctly %s' % str(self.customsOfficeItem))
     90         self.planet = sm.GetService('planetSvc').GetPlanet(self.customsOfficeItem.planetID)
     91         sm.GetService('inv').Register(self)
     92         blue.pyos.synchro.Yield()
     93         self.Layout()
     94         self._OnResize()
     95         self.ResetContents()
     96 
     97     def Layout(self):
     98         self.ConstructTopParent()
     99         self.SetCaption(localization.GetByLabel('UI/PI/Common/PlanetaryCustomsOfficeName', planetName=cfg.evelocations.Get(self.planet.planetID).name))
    100         self.footer = ContainerAutoSize(name='footer', parent=self.sr.main, align=uiconst.TOBOTTOM, pos=(0, 0, 0, 25), padding=(0, 8, 0, 0), alignMode=uiconst.TOPRIGHT)
    101         self.cols = Container(name='colums', parent=self.sr.main, align=uiconst.TOALL)
    102         self.leftColumn = Container(name='leftColumn', parent=self.cols, align=uiconst.TOLEFT_PROP, width=0.5, clipChildren=True)
    103         self.rightColumn = Container(name='rightColumn', parent=self.cols, align=uiconst.TOALL, padding=(16, 0, 0, 0), clipChildren=True)
    104         colTopHeight = 70
    105         self.customsHeader = Container(name='customsHeader', parent=self.leftColumn, align=uiconst.TOTOP, height=colTopHeight)
    106         self.customsList = Container(name='customsList', parent=self.leftColumn, align=uiconst.TOALL, state=uiconst.UI_PICKCHILDREN)
    107         self.customsHeaderTitle = eveLabel.Label(text=localization.GetByLabel('UI/PI/Common/PlanetaryCustomsOffice'), parent=self.customsHeader, align=uiconst.TOPLEFT, fontsize=16, state=uiconst.UI_NORMAL)
    108         self.customsGauge = Gauge(parent=self.customsHeader, value=0.0, color=planetCommonUI.PLANET_COLOR_STORAGE, pos=(0,
    109          25,
    110          GAUGE_WIDTH,
    111          0), state=uiconst.UI_HIDDEN, align=uiconst.TOPLEFT)
    112         self.spaceportHeader = Container(name='spaceportHeader', parent=self.rightColumn, align=uiconst.TOTOP, height=colTopHeight)
    113         self.spaceportList = Container(name='spaceportList', parent=self.rightColumn, align=uiconst.TOALL, state=uiconst.UI_PICKCHILDREN)
    114         self.spaceportCombo = Combo(label=None, parent=self.spaceportHeader, options=[], name='imex_import_select', callback=self.OnSpaceportComboChanged, width=GAUGE_WIDTH, align=uiconst.TOPRIGHT)
    115         self.spaceportGauge = Gauge(parent=self.spaceportHeader, value=0.0, color=planetCommonUI.PLANET_COLOR_STORAGE, pos=(0,
    116          self.spaceportCombo.height + 4,
    117          GAUGE_WIDTH,
    118          0), state=uiconst.UI_HIDDEN, align=uiconst.TOPRIGHT)
    119         self.transferCostLabel = eveLabel.EveLabelSmall(parent=self.footer, state=uiconst.UI_NORMAL, top=7, align=uiconst.CENTERLEFT)
    120         btns = [(localization.GetByLabel('UI/PI/Common/CustomsOfficeTransfer'),
    121           self.ConfirmCommodityTransfer,
    122           (),
    123           None)]
    124         btns = ButtonGroup(btns=btns, parent=self.footer, align=uiconst.TOPRIGHT)
    125         self.transferBtn = btns.buttons[0]
    126         self.customsListScroll = eveScroll.Scroll(parent=self.customsList, name='customsList')
    127         self.customsListScroll.sr.id = 'piCustomsListScroll'
    128         self.spaceportListScroll = eveScroll.Scroll(parent=self.spaceportList, name='spaceportList')
    129         self.spaceportListScroll.sr.id = 'piSpaceportListScroll'
    130 
    131     def ConstructTopParent(self):
    132         self.windowCaption = EveHeaderMedium(parent=self.sr.main, align=uiconst.TOTOP, padBottom=8)
    133 
    134     def ConstructTopParentOld(self):
    135         topParent = Container(name='topParent', parent=self.GetMainArea(), align=uiconst.TOTOP, height=56, clipChildren=True)
    136         SpriteThemeColored(name='mainicon', parent=topParent, state=uiconst.UI_DISABLED, pos=(0, -3, 64, 64), texturePath=self.iconNum, colorType=uiconst.COLORTYPE_UIHILIGHTGLOW)
    137         self.windowCaption = eveLabel.WndCaptionLabel(text=localization.GetByLabel('UI/PI/Common/PlanetaryCustomsOfficeName', planetName=cfg.evelocations.Get(self.planet.planetID).name), subcaption=localization.GetByLabel('UI/PI/Common/ImportExportSubHeading'), parent=topParent)
    138 
    139     def IsAllowedToConfigureForCorp(self):
    140         checkIsMyCorps = self.customsOfficeItem.ownerID == session.corpid
    141         checkIsStationManager = session.corprole & const.corpRoleStationManager == const.corpRoleStationManager
    142         hasAccess = checkIsMyCorps and checkIsStationManager
    143         return hasAccess
    144 
    145     def ResetContents(self):
    146         self.LoadDestComboOptions()
    147         self.SetCustomsOfficeContent()
    148         self.SetSpaceportContent()
    149         self.importContents = {}
    150         self.exportContents = {}
    151         self.UpdateTaxRate()
    152         self.RefreshLists()
    153 
    154     def RefreshLists(self, *args):
    155         self.LoadContentToScroll(self.customsOfficeContents, self.exportContents, self.customsListScroll, self.OnCustomsScrollDropData)
    156         self.LoadContentToScroll(self.spaceportContents, self.importContents, self.spaceportListScroll, self.OnSpaceportScrollDropData)
    157         self.RefreshHeaderInfo()
    158 
    159     def GetSettingsMenu(self, *args):
    160         return [(localization.GetByLabel('UI/DustLink/ConfigureOrbital'), self.OpenConfiguration, ())]
    161 
    162     def GetMenuMoreOptions(self):
    163         menuData = super(PlanetaryImportExportUI, self).GetMenuMoreOptions()
    164         if self.IsAllowedToConfigureForCorp():
    165             menuData += self.GetSettingsMenu()
    166         return menuData
    167 
    168     def OpenConfiguration(self):
    169         OrbitalConfigurationWindow.Open(orbitalItem=self.customsOffice.GetItem())
    170 
    171     def LoadDestComboOptions(self):
    172         colony = self.planet.GetColony(session.charid)
    173         if colony is None:
    174             self.spaceportCombo.LoadOptions([(localization.GetByLabel('UI/PI/Common/NoDestinationsFound'), None)])
    175             return
    176         self.endpoints = colony.GetImportEndpoints()
    177         if len(self.endpoints) < 1:
    178             self.spaceportCombo.LoadOptions([(localization.GetByLabel('UI/PI/Common/NoDestinationsFound'), None)])
    179             return
    180         options = []
    181         for endpoint in self.endpoints:
    182             pin = self.planet.GetPin(endpoint.id)
    183             options.append((planetCommon.GetGenericPinName(pin.typeID, pin.id), endpoint.id))
    184 
    185         if self.spaceportPinID is None:
    186             self.spaceportPinID = options[0][1]
    187         self.spaceportCombo.LoadOptions(options, select=self.spaceportPinID)
    188 
    189     def SetCustomsOfficeContent(self):
    190         self.customsOfficeContents = {}
    191         for item in self.customsOffice.List(const.flagHangar):
    192             if item.flagID != const.flagHangar:
    193                 continue
    194             if item.ownerID != session.charid:
    195                 continue
    196             self.customsOfficeContents[item.itemID, item.typeID] = utillib.KeyVal(itemID=item.itemID, typeID=item.typeID, quantity=item.stacksize, name=planetCommonUI.GetProductNameAndTier(item.typeID), item=getattr(item, 'item', item))
    197 
    198     def SetSpaceportContent(self):
    199         self.spaceportContents = {}
    200         if self.spaceportPinID is None:
    201             return
    202         if self.planet.GetColony(session.charid) is None:
    203             log.LogWarn('Unable to update spaceport contents, colony not yet loaded')
    204             return
    205         pin = self.planet.GetPin(self.spaceportPinID)
    206         if pin is None:
    207             raise UserError('CannotImportEndpointNotFound')
    208         for typeID, qty in pin.GetContents().iteritems():
    209             name = planetCommonUI.GetProductNameAndTier(typeID)
    210             self.spaceportContents[None, typeID] = utillib.KeyVal(itemID=None, typeID=typeID, quantity=qty, name=name)
    211 
    212     def LoadContentToScroll(self, contentList, transferList, scroll, onDropDataCallback):
    213         scroll.sr.content.OnDropData = onDropDataCallback
    214         scrollHeaders = ['', localization.GetByLabel('UI/Common/Quantity'), localization.GetByLabel('UI/Common/Commodity')]
    215         scrollContents = []
    216         scrollNoContentText = localization.GetByLabel('UI/PI/Common/NoItemsFound')
    217         for item in contentList.values():
    218             data = utillib.KeyVal()
    219             data.label = '<t>%d<t>%s' % (item.quantity, item.name)
    220             data.quantity = item.quantity
    221             data.typeID = item.typeID
    222             data.itemID = item.itemID
    223             data.getIcon = 1
    224             data.hint = item.name
    225             data.item = getattr(item, 'item', None)
    226             data.isItemTransfer = (item.itemID, item.typeID) in transferList
    227             data.OnDropData = onDropDataCallback
    228             scrollContents.append(GetFromClass(CustomsItem, data))
    229 
    230         sortBy = scroll.GetSortBy()
    231         if sortBy is None:
    232             sortBy = localization.GetByLabel('UI/Common/Commodity')
    233         scroll.LoadContent(contentList=scrollContents, headers=scrollHeaders, noContentHint=scrollNoContentText, sortby=sortBy)
    234         scroll.RefreshSort()
    235 
    236     def GetCommodityVolume(self, commodities = None, excluded = None):
    237         volume = 0
    238         for key, item in commodities.iteritems():
    239             if excluded is None or key not in excluded:
    240                 volume += GetTypeVolume(key[1], item.quantity)
    241 
    242         return volume
    243 
    244     def GetCustomsCapacityUsed(self):
    245         capacity = self.customsOffice.GetCapacity().used
    246         for item in self.exportContents.values():
    247             capacity += GetTypeVolume(item.typeID, item.quantity)
    248 
    249         return capacity
    250 
    251     def GetCustomsCapacityTotal(self):
    252         return self.customsOffice.GetCapacity().capacity
    253 
    254     def GetCustomsCapacityAvailable(self):
    255         used = self.GetCustomsCapacityUsed()
    256         capacity = self.GetCustomsCapacityTotal()
    257         return capacity - used
    258 
    259     def CheckAvailableSpaceInCustoms(self, commodities = None):
    260         available = self.GetCustomsCapacityAvailable()
    261         required = self.GetCommodityVolume(commodities, self.importContents)
    262         if required - available > 1e-05:
    263             raise UserError('NotEnoughSpace', {'volume': required,
    264              'available': available})
    265 
    266     def GetSpaceportCapacityUsed(self):
    267         pin = self.planet.GetPin(self.spaceportPinID)
    268         if not pin:
    269             return 0
    270         capacity = pin.capacityUsed
    271         for item in self.importContents.values():
    272             capacity += GetTypeVolume(item.typeID, item.quantity)
    273 
    274         return capacity
    275 
    276     def GetSpaceportCapacityTotal(self):
    277         pin = self.planet.GetPin(self.spaceportPinID)
    278         if not pin:
    279             return 0
    280         return pin.GetCapacity()
    281 
    282     def IsItemHere(self, item):
    283         return item.itemID == self.customsOfficeID
    284 
    285     def GetSpaceportCapacityAvailable(self):
    286         used = self.GetSpaceportCapacityUsed()
    287         capacity = self.GetSpaceportCapacityTotal()
    288         return capacity - used
    289 
    290     def CheckAvailableSpaceInSpaceport(self, commodities = None):
    291         available = self.GetSpaceportCapacityAvailable()
    292         required = self.GetCommodityVolume(commodities, self.exportContents)
    293         if required - available > 1e-05:
    294             raise UserError('NotEnoughSpace', {'volume': required,
    295              'available': available})
    296 
    297     def RefreshHeaderInfo(self):
    298         self.spaceportGauge.state = uiconst.UI_HIDDEN
    299         if self.spaceportPinID is not None:
    300             pin = self.planet.GetPin(self.spaceportPinID)
    301             if not pin:
    302                 return
    303             capacityUsed = self.GetSpaceportCapacityUsed()
    304             capacityMax = self.GetSpaceportCapacityTotal()
    305             self.spaceportGauge.state = uiconst.UI_DISABLED
    306             self.spaceportGauge.SetSubText(localization.GetByLabel('UI/PI/Common/StorageUsed', capacityUsed=capacityUsed, capacityMax=capacityMax))
    307             self.spaceportGauge.SetValue(capacityUsed / capacityMax)
    308         self.customsGauge.state = uiconst.UI_HIDDEN
    309         if self.customsOffice is not None:
    310             capacityUsed = self.GetCustomsCapacityUsed()
    311             capacityMax = self.GetCustomsCapacityTotal()
    312             self.customsGauge.state = uiconst.UI_DISABLED
    313             self.customsGauge.SetSubText(localization.GetByLabel('UI/PI/Common/StorageUsed', capacityUsed=capacityUsed, capacityMax=capacityMax))
    314             self.customsGauge.SetValue(capacityUsed / capacityMax)
    315         self.RefreshCostText()
    316 
    317     def GetCommodities(self, source):
    318         commods = {}
    319         for itemVoucher in source.itervalues():
    320             if itemVoucher.typeID not in commods:
    321                 commods[itemVoucher.typeID] = itemVoucher.quantity
    322             else:
    323                 commods[itemVoucher.typeID] += itemVoucher.quantity
    324 
    325         return commods
    326 
    327     def GetCost(self):
    328         cost = None
    329         pin = self.planet.GetPin(self.spaceportPinID)
    330         if pin is not None and self.taxRate is not None:
    331             cost = pin.GetExportTax(self.GetCommodities(self.exportContents), self.taxRate)
    332             cost += pin.GetImportTax(self.GetCommodities(self.importContents), self.taxRate)
    333         return cost
    334 
    335     def RefreshCostText(self):
    336         cost = self.GetCost()
    337         if cost is not None:
    338             costStr = FmtISK(cost)
    339             if cost > 0:
    340                 costStr = '<color=red>%s</color>' % costStr
    341             self.transferCostLabel.text = localization.GetByLabel('UI/PI/Common/TransferCost', iskAmount=costStr)
    342         if self.taxRate is not None:
    343             self.windowCaption.text = localization.GetByLabel('UI/PI/Common/CustomsOfficeTaxRate', taxRate=self.taxRate * 100)
    344         else:
    345             self.windowCaption.text = localization.GetByLabel('UI/PI/Common/CustomsOfficeAccessDenied')
    346 
    347     def UpdateTaxRate(self):
    348         self.taxRate = eveMoniker.GetPlanetOrbitalRegistry(session.solarsystemid).GetTaxRate(self.id)
    349         self.RefreshCostText()
    350 
    351     def OnSpaceportComboChanged(self, comboItem, spaceportName, spaceportPinID, *args):
    352         if self.spaceportPinID != spaceportPinID:
    353             self.spaceportPinID = spaceportPinID
    354             self.ResetContents()
    355 
    356     def OnItemChange(self, item, change, location):
    357         locationIdx = const.ixLocationID
    358         if self.id not in (item[locationIdx], change.get(locationIdx, 'No location change')):
    359             return
    360         self.ResetContents()
    361 
    362     def OnRefreshPins(self, pinIDs):
    363         if not self or self.destroyed:
    364             return
    365         if self.spaceportPinID in pinIDs:
    366             self.ResetContents()
    367 
    368     def OnPlanetPinsChanged(self, planetID):
    369         if self.planet.planetID == planetID:
    370             self.ResetContents()
    371 
    372     def OnPlanetChangesSubmitted(self, planetID):
    373         if self.planet.planetID != planetID:
    374             return
    375         colony = self.planet.GetColony(session.charid)
    376         if not colony.GetImportEndpoints():
    377             self.CloseByUser()
    378         else:
    379             self.ResetContents()
    380 
    381     def OnSkyhookFirstActivation(self, skyhookID, planetID):
    382         if self.planet.planetID == planetID:
    383             self.Close()
    384             prompt_player_fsd_dialog('SkyhookReplacingPoco')
    385 
    386     def OnCustomsScrollDropData(self, dragObj, nodes, *args):
    387         scroll = getattr(nodes[0], 'scroll', None)
    388         if not scroll:
    389             return
    390         if scroll.name == 'spaceportList':
    391             self.ExportCommodity(nodes)
    392         elif scroll.name != 'customsList':
    393             self.MoveFromInventory(nodes)
    394 
    395     def OnSpaceportScrollDropData(self, dragObj, nodes, *args):
    396         scroll = getattr(nodes[0], 'scroll', None)
    397         if not scroll:
    398             return
    399         if scroll.name == 'customsList':
    400             self.ImportCommodity(nodes)
    401         else:
    402             raise UserError('CannotDropItemsOntoSpaceport')
    403 
    404     def ImportCommodity(self, nodes):
    405         if not self.spaceportPinID:
    406             raise UserError('NoSpaceportsAvailable')
    407         if self.taxRate is None:
    408             raise UserError('PortStandingCheckFail', {'corpName': (const.UE_OWNERID, self.customsOfficeItem.ownerID)})
    409         items = self.CommoditiesToTransfer(nodes, toSpaceport=True)
    410         for key, item in items.iteritems():
    411             self.RemoveStuff(key, item, self.customsOfficeContents)
    412             self.AddStuff(key, item, self.spaceportContents)
    413             if key in self.exportContents:
    414                 self.RemoveStuff(key, item, self.exportContents)
    415             else:
    416                 self.AddStuff(key, item, self.importContents)
    417 
    418         self.RefreshLists()
    419 
    420     def ExportCommodity(self, nodes):
    421         if self.taxRate is None:
    422             raise UserError('PortStandingCheckFail', {'corpName': (const.UE_OWNERID, self.customsOfficeItem.ownerID)})
    423         items = self.CommoditiesToTransfer(nodes)
    424         for key, item in items.iteritems():
    425             self.RemoveStuff(key, item, self.spaceportContents)
    426             self.AddStuff(key, item, self.customsOfficeContents)
    427             if key in self.importContents:
    428                 self.RemoveStuff(key, item, self.importContents)
    429             else:
    430                 self.AddStuff(key, item, self.exportContents)
    431 
    432         self.RefreshLists()
    433 
    434     def CommoditiesToTransfer(self, commodities, toSpaceport = False):
    435         toMove = {}
    436         for item in commodities:
    437             toMove[item.itemID, item.typeID] = utillib.KeyVal(name=planetCommonUI.GetProductNameAndTier(item.typeID), itemID=item.itemID, typeID=item.typeID, quantity=item.quantity, item=getattr(item, 'item', item))
    438 
    439         if len(commodities) == 1:
    440             if toSpaceport:
    441                 available = self.GetSpaceportCapacityAvailable()
    442                 required = self.GetCommodityVolume(toMove, self.exportContents)
    443             else:
    444                 available = self.GetCustomsCapacityAvailable()
    445                 required = self.GetCommodityVolume(toMove, self.importContents)
    446             if required > 0 and uicore.uilib.Key(uiconst.VK_SHIFT) or required - available > 1e-05:
    447                 selectedItem = commodities[0]
    448                 itemID = selectedItem.itemID
    449                 typeID = selectedItem.typeID
    450                 typeName = planetCommonUI.GetProductNameAndTier(typeID)
    451                 availableQuantity = min(selectedItem.quantity, int(available / required * selectedItem.quantity))
    452                 if availableQuantity > 0:
    453                     ret = uix.QtyPopup(availableQuantity, 1, availableQuantity, None, localization.GetByLabel('UI/PI/Common/QuantityToTransfer', typeName=typeName))
    454                     if ret and 'qty' in ret:
    455                         toMove[itemID, typeID].quantity = min(availableQuantity, max(1, ret['qty']))
    456                     else:
    457                         toMove = {}
    458         if toSpaceport:
    459             self.CheckAvailableSpaceInSpaceport(toMove)
    460         else:
    461             self.CheckAvailableSpaceInCustoms(toMove)
    462         return toMove
    463 
    464     def AddStuff(self, key, item, toDict):
    465         if key not in toDict:
    466             toDict[key] = utillib.KeyVal(itemID=item.itemID, typeID=item.typeID, quantity=item.quantity, name=item.name, item=item.item)
    467         else:
    468             toDict[key].quantity += item.quantity
    469 
    470     def RemoveStuff(self, key, item, fromDict):
    471         if key in fromDict:
    472             fromDict[key].quantity -= item.quantity
    473             if fromDict[key].quantity <= 0:
    474                 del fromDict[key]
    475 
    476     def MoveFromInventory(self, nodes):
    477         allowableSources = ('xtriui.InvItem', 'listentry.InvItem')
    478         items = [ item for item in nodes if item.Get('__guid__', None) in allowableSources ]
    479         if len(items) > 0:
    480             sourceLocation = items[0].rec.locationID
    481             items = self.CommoditiesToTransfer([ item.item for item in items ])
    482             for key, item in items.iteritems():
    483                 self.customsOffice.Add(key[0], sourceLocation, qty=item.quantity)
    484 
    485     def ConfirmCommodityTransfer(self, *args):
    486         if self.spaceportPinID is None:
    487             raise UserError('CannotImportEndpointNotFound')
    488         planet = sm.GetService('planetUI').GetCurrentPlanet()
    489         if planet is not None and planet.IsInEditMode():
    490             raise UserError('CannotImportExportInEditMode')
    491         if len(self.importContents) + len(self.exportContents) < 1:
    492             raise UserError('PleaseSelectCommoditiesToImport')
    493         importData = {key[0]:value.quantity for key, value in self.importContents.iteritems()}
    494         exportData = {key[1]:value.quantity for key, value in self.exportContents.iteritems()}
    495         try:
    496             customsOfficeInventory = sm.GetService('invCache').GetInventoryFromId(self.customsOfficeID)
    497             customsOfficeInventory.ImportExportWithPlanet(self.spaceportPinID, importData, exportData, self.taxRate)
    498         except UserError as e:
    499             if e.msg != 'TaxChanged':
    500                 raise
    501             self.UpdateTaxRate()
    502             if self.taxRate is None:
    503                 self.ResetContents()
    504                 raise UserError('PortStandingCheckFail', {'corpName': (const.UE_OWNERID, self.customsOfficeItem.ownerID)})
    505             if prompt_player_fsd_dialog('CustomsOfficeTaxRateChanged', {'cost': self.GetCost()}, uiconst.YESNO) == uiconst.ID_YES:
    506                 customsOfficeInventory.ImportExportWithPlanet(self.spaceportPinID, importData, exportData, self.taxRate)
    507             else:
    508                 self.ResetContents()