Beispiel #1
0
 private void ExtractExtraCharacterAppearance(PyDictionary data, out PyInteger accessoryID,
                                              out PyInteger beardID, out PyInteger decoID, out PyInteger lipstickID, out PyInteger makeupID,
                                              out PyDecimal morph1e, out PyDecimal morph1n, out PyDecimal morph1s, out PyDecimal morph1w,
                                              out PyDecimal morph2e, out PyDecimal morph2n, out PyDecimal morph2s, out PyDecimal morph2w,
                                              out PyDecimal morph3e, out PyDecimal morph3n, out PyDecimal morph3s, out PyDecimal morph3w,
                                              out PyDecimal morph4e, out PyDecimal morph4n, out PyDecimal morph4s, out PyDecimal morph4w)
 {
     data.TryGetValue("accessoryID", out accessoryID);
     data.TryGetValue("beardID", out beardID);
     data.TryGetValue("decoID", out decoID);
     data.TryGetValue("lipstickID", out lipstickID);
     data.TryGetValue("makeupID", out makeupID);
     data.TryGetValue("morph1e", out morph1e);
     data.TryGetValue("morph1n", out morph1n);
     data.TryGetValue("morph1s", out morph1s);
     data.TryGetValue("morph1w", out morph1w);
     data.TryGetValue("morph2e", out morph2e);
     data.TryGetValue("morph2n", out morph2n);
     data.TryGetValue("morph2s", out morph2s);
     data.TryGetValue("morph2w", out morph2w);
     data.TryGetValue("morph3e", out morph3e);
     data.TryGetValue("morph3n", out morph3n);
     data.TryGetValue("morph3s", out morph3s);
     data.TryGetValue("morph3w", out morph3w);
     data.TryGetValue("morph4e", out morph4e);
     data.TryGetValue("morph4n", out morph4n);
     data.TryGetValue("morph4s", out morph4s);
     data.TryGetValue("morph4w", out morph4w);
 }
Beispiel #2
0
        public void DecimalMarshal_Value()
        {
            PyDecimal value = new PyDecimal(sDecimal_Value);

            byte[] output = Marshal.Marshal.ToByteArray(value, false);

            Assert.AreEqual(sDecimal_ValueBuffer, output);
        }
Beispiel #3
0
        public void DecimalUnmarshal_Value()
        {
            PyDataType value = Unmarshal.ReadFromByteArray(sDecimal_ValueBuffer, false);

            Assert.IsInstanceOf <PyDecimal>(value);

            PyDecimal @decimal = value as PyDecimal;

            Assert.AreEqual(sDecimal_Value, @decimal.Value);
        }
Beispiel #4
0
        public PyDataType InsureShip(PyInteger itemID, PyDecimal insuranceCost, PyInteger isCorpItem, CallInformation call)
        {
            int callerCharacterID = call.Client.EnsureCharacterIsSelected();

            if (this.ItemManager.IsItemLoaded(itemID) == false)
            {
                throw new CustomError("Ships not loaded for player and hangar!");
            }

            Ship      item      = this.ItemManager.GetItem(itemID) as Ship;
            Character character = this.ItemManager.GetItem(callerCharacterID) as Character;

            if ((isCorpItem == 1 && item.OwnerID != call.Client.CorporationID) && item.OwnerID != callerCharacterID)
            {
                throw new MktNotOwner();
            }

            if (item.Singleton == false)
            {
                throw new InsureShipFailed("Only assembled ships can be insured");
            }

            string ownerName = "";

            if (this.DB.IsShipInsured(item.ID, out ownerName) == true)
            {
                throw new InsureShipFailedSingleContract(ownerName);
            }

            // check the user has enough money
            character.EnsureEnoughBalance(insuranceCost);

            // subtract the money off the character
            character.Balance -= insuranceCost;

            double fraction = insuranceCost * 100 / item.Type.BasePrice;

            // create insurance record
            this.DB.InsureShip(item.ID, isCorpItem == 0 ? callerCharacterID : call.Client.CorporationID, fraction / 5);

            this.MarketDB.CreateJournalForCharacter(
                MarketReference.Insurance, character.ID, this.ItemManager.SecureCommerceCommision.ID, -item.ID,
                -insuranceCost, character.Balance, $"Insurance fee for {item.Name}", 1000
                );

            // send the notification to the user
            call.Client.NotifyBalanceUpdate(character.Balance);

            // persist changes in the balance
            character.Persist();

            // TODO: CHECK IF THE INSURANCE SHOULD BE CHARGED TO THE CORP

            return(true);
        }
Beispiel #5
0
 /// <summary>
 /// Converts the given <paramref name="data"/> to it's byte array representation.
 /// Decimals are encoded based on their value
 ///
 /// The following opcodes are supported
 /// <seealso cref="Opcode.RealZero" /> 1 byte, value = 0.0
 /// <seealso cref="Opcode.Real" /> 9 bytes, value is eight bytes after the opcode, double
 /// </summary>
 /// <param name="writer">Where to write the encoded data to</param>
 /// <param name="data">The value to write</param>
 private static void ProcessDecimal(BinaryWriter writer, PyDecimal data)
 {
     if (data == 0.0)
     {
         writer.WriteOpcode(Opcode.RealZero);
     }
     else
     {
         writer.WriteOpcode(Opcode.Real);
         writer.Write(data.Value);
     }
 }
Beispiel #6
0
        public PyDataType SetPlayerStanding(PyInteger characterID, PyDecimal standing, PyString reason, CallInformation call)
        {
            int callerCharacterID = call.Client.EnsureCharacterIsSelected();

            this.DB.CreateStandingTransaction((int)EventType.StandingPlayerSetStanding, callerCharacterID, characterID, standing, reason);
            this.DB.SetPlayerStanding(callerCharacterID, characterID, standing);

            // send the same notification to both players
            this.NotificationManager.NotifyCharacters(
                new PyList <PyInteger>(2)
            {
                [0] = callerCharacterID, [1] = characterID
            },
                new OnStandingSet(callerCharacterID, characterID, standing)
                );

            return(null);
        }
Beispiel #7
0
        public PyDataType SetPlayerStanding(PyInteger characterID, PyDecimal standing, PyString reason, CallInformation call)
        {
            int callerCharacterID = call.Client.EnsureCharacterIsSelected();

            this.DB.CreateStandingTransaction((int)StandingEventType.StandingPlayerSetStanding, callerCharacterID, characterID, standing, reason);
            this.DB.SetPlayerStanding(callerCharacterID, characterID, standing);

            // send standing change notification to the player
            PyTuple notification = new PyTuple(3)
            {
                [0] = callerCharacterID,
                [1] = characterID,
                [2] = standing
            };

            // send the same notification to both players
            call.Client.ClusterConnection.SendNotification("OnStandingSet", "charid", callerCharacterID, call.Client, notification);
            call.Client.ClusterConnection.SendNotification("OnStandingSet", "charid", characterID, notification);

            return(null);
        }
Beispiel #8
0
        public PyDataType PlaceCharOrder(PyInteger stationID, PyInteger typeID, PyDecimal price, PyInteger quantity,
                                         PyInteger bid, PyInteger range, PyDataType itemID, PyInteger minVolume, PyInteger duration, PyBool useCorp,
                                         PyDataType located, CallInformation call)
        {
            // get solarSystem for the station
            Character character  = this.ItemFactory.GetItem <Character>(call.Client.EnsureCharacterIsSelected());
            double    brokerCost = 0.0;

            // if the order is not immediate check the amount of orders the character has
            if (duration != 0)
            {
                int maximumOrders = this.GetMaxOrderCountForCharacter(character);
                int currentOrders = this.DB.CountCharsOrders(character.ID);

                if (maximumOrders <= currentOrders)
                {
                    throw new MarketExceededOrderCount(currentOrders, maximumOrders);
                }

                // calculate broker costs for the order
                this.CalculateBrokerCost(character.GetSkillLevel(Types.BrokerRelations), quantity, price, out brokerCost);
            }

            // check if the character has the Marketing skill and calculate distances
            if (bid == (int)TransactionType.Sell)
            {
                if (itemID is PyInteger == false)
                {
                    throw new CustomError("Unexpected data!");
                }

                this.PlaceSellOrderChar(itemID as PyInteger, character, stationID, quantity, typeID, duration, price, range, brokerCost, call);
            }
            else if (bid == (int)TransactionType.Buy)
            {
                this.PlaceBuyOrderChar(typeID, character, stationID, quantity, price, duration, minVolume, range, brokerCost, call);
            }

            return(null);
        }
Beispiel #9
0
 private void ExtractCharacterAppearance(PyDictionary data, out PyInteger costumeID, out PyInteger eyebrowsID,
                                         out PyInteger eyesID, out PyInteger hairID, out PyInteger skinID, out PyInteger backgroundID,
                                         out PyInteger lightID, out PyDecimal headRotation1, out PyDecimal headRotation2,
                                         out PyDecimal headRotation3, out PyDecimal eyeRotation1, out PyDecimal eyeRotation2,
                                         out PyDecimal eyeRotation3, out PyDecimal camPos1, out PyDecimal camPos2, out PyDecimal camPos3)
 {
     if (data.TryGetValue("costumeID", out costumeID) == false)
     {
         throw new Exception("Cannot get costumeID from character appearance");
     }
     if (data.TryGetValue("eyebrowsID", out eyebrowsID) == false)
     {
         throw new Exception("Cannot get eyebrowsID from character appearance");
     }
     if (data.TryGetValue("eyesID", out eyesID) == false)
     {
         throw new Exception("Cannot get eyesID from character appearance");
     }
     if (data.TryGetValue("hairID", out hairID) == false)
     {
         throw new Exception("Cannot get hairID from character appearance");
     }
     if (data.TryGetValue("skinID", out skinID) == false)
     {
         throw new Exception("Cannot get skinID from character appearance");
     }
     if (data.TryGetValue("backgroundID", out backgroundID) == false)
     {
         throw new Exception("Cannot get backgroundID from character appearance");
     }
     if (data.TryGetValue("lightID", out lightID) == false)
     {
         throw new Exception("Cannot get lightID from character appearance");
     }
     if (data.TryGetValue("headRotation1", out headRotation1) == false)
     {
         throw new Exception("Cannot get headRotation1 from character appearance");
     }
     if (data.TryGetValue("headRotation2", out headRotation2) == false)
     {
         throw new Exception("Cannot get headRotation2 from character appearance");
     }
     if (data.TryGetValue("headRotation3", out headRotation3) == false)
     {
         throw new Exception("Cannot get headRotation3 from character appearance");
     }
     if (data.TryGetValue("eyeRotation1", out eyeRotation1) == false)
     {
         throw new Exception("Cannot get eyeRotation1 from character appearance");
     }
     if (data.TryGetValue("eyeRotation2", out eyeRotation2) == false)
     {
         throw new Exception("Cannot get eyeRotation2 from character appearance");
     }
     if (data.TryGetValue("eyeRotation3", out eyeRotation3) == false)
     {
         throw new Exception("Cannot get eyeRotation3 from character appearance");
     }
     if (data.TryGetValue("camPos1", out camPos1) == false)
     {
         throw new Exception("Cannot get camPos1 from character appearance");
     }
     if (data.TryGetValue("camPos2", out camPos2) == false)
     {
         throw new Exception("Cannot get camPos2 from character appearance");
     }
     if (data.TryGetValue("camPos3", out camPos3) == false)
     {
         throw new Exception("Cannot get camPos3 from character appearance");
     }
 }
Beispiel #10
0
 private void ProcessDecimal(PyDecimal dec)
 {
     this.mStringBuilder.AppendFormat("[PyDecimal {0}]", dec.Value);
     this.mStringBuilder.AppendLine();
 }
Beispiel #11
0
 protected bool Equals(PyDecimal other)
 {
     return(Value.Equals(other.Value));
 }
Beispiel #12
0
        /// <summary>
        /// <seealso cref="Marshal.ProcessPackedRow"/>
        ///
        /// Opcodes supported:
        /// <seealso cref="Opcode.PackedRow"/>
        /// </summary>
        /// <param name="opcode">Type of object to parse</param>
        /// <returns>The decoded python type</returns>
        /// <exception cref="InvalidDataException">If any error was found in the data</exception>
        private PyDataType ProcessPackedRow(Opcode opcode)
        {
            if (opcode != Opcode.PackedRow)
            {
                throw new InvalidDataException($"Trying to parse a {opcode} as PackedRow");
            }

            DBRowDescriptor descriptor = this.Process(false);

            PyDataType[] data = new PyDataType[descriptor.Columns.Count];

            MemoryStream decompressedStream = ZeroCompressionUtils.LoadZeroCompressed(this.mReader);
            BinaryReader decompressedReader = new BinaryReader(decompressedStream);

            // sort columns by the bit size
            IEnumerable <DBRowDescriptor.Column> enumerator =
                descriptor.Columns.OrderByDescending(c => Utils.GetTypeBits(c.Type));

            int  bitOffset = 8;
            byte buffer    = 0;
            int  index     = 0;

            foreach (DBRowDescriptor.Column column in enumerator)
            {
                switch (column.Type)
                {
                case FieldType.I8:
                case FieldType.UI8:
                case FieldType.CY:
                case FieldType.FileTime:
                    data[index++] = new PyInteger(decompressedReader.ReadInt64());
                    break;

                case FieldType.I4:
                case FieldType.UI4:
                    data[index++] = new PyInteger(decompressedReader.ReadInt32());
                    break;

                case FieldType.I2:
                case FieldType.UI2:
                    data[index++] = new PyInteger(decompressedReader.ReadInt16());
                    break;

                case FieldType.I1:
                case FieldType.UI1:
                    data[index++] = new PyInteger(decompressedReader.ReadByte());
                    break;

                case FieldType.R8:
                    data[index++] = new PyDecimal(decompressedReader.ReadDouble());
                    break;

                case FieldType.R4:
                    data[index++] = new PyDecimal(decompressedReader.ReadSingle());
                    break;

                case FieldType.Bool:
                    // read a byte from the buffer if needed
                    if (bitOffset == 8)
                    {
                        buffer    = decompressedReader.ReadByte();
                        bitOffset = 0;
                    }

                    data[index++] = new PyBool(((buffer >> bitOffset++) & 0x01) == 0x01);
                    break;

                case FieldType.Bytes:
                case FieldType.WStr:
                case FieldType.Str:
                    data[index++] = this.Process(false);
                    break;
                }
            }

            return(new PyPackedRow(descriptor, data));
        }
Beispiel #13
0
        public PyDataType ModifyCharOrder(PyInteger orderID, PyDecimal newPrice, PyInteger bid, PyInteger stationID, PyInteger solarSystemID, PyDecimal price, PyInteger volRemaining, PyInteger issued, CallInformation call)
        {
            int callerCharacterID = call.Client.EnsureCharacterIsSelected();

            Character character = this.ItemFactory.GetItem <Character>(callerCharacterID);

            using MySqlConnection connection = this.DB.AcquireMarketLock();
            try
            {
                MarketOrder order = this.DB.GetOrderById(connection, orderID);

                if (order.CharacterID != callerCharacterID)
                {
                    throw new MktOrderDidNotMatch();
                }

                long currentTime = DateTime.UtcNow.ToFileTimeUtc();
                // check for timers, no changes in less than 5 minutes
                if (currentTime < order.Issued + TimeSpan.TicksPerSecond * this.NodeContainer.Constants[Constants.mktModificationDelay])
                {
                    throw new MktOrderDelay((order.Issued + TimeSpan.TicksPerSecond * this.NodeContainer.Constants[Constants.mktModificationDelay]) - currentTime);
                }

                // ensure the order hasn't been modified since the user saw it on the screen
                if ((int)order.Bid != bid || order.LocationID != stationID || order.Price != price ||
                    order.UnitsLeft != volRemaining || order.Issued != issued)
                {
                    throw new MktOrderDidNotMatch();
                }

                // get the modification broker's fee
                double brokerCost = 0.0;
                double newEscrow  = 0.0;

                this.CalculateBrokerCost(character.GetSkillLevel(Types.BrokerRelations), volRemaining, (newPrice - price), out brokerCost);

                using Wallet wallet = this.WalletManager.AcquireWallet(order.CharacterID, order.AccountID);
                {
                    if (order.Bid == TransactionType.Buy)
                    {
                        // calculate the difference in escrow
                        newEscrow = volRemaining * newPrice;
                        double escrowDiff = order.Escrow - newEscrow;

                        // ensure enough balances
                        wallet.EnsureEnoughBalance(escrowDiff + brokerCost);
                        // take the difference in escrow
                        wallet.CreateJournalRecord(MarketReference.MarketEscrow, null, null, escrowDiff);
                    }
                    else
                    {
                        wallet.EnsureEnoughBalance(brokerCost);
                    }

                    // pay the broker fee once again
                    wallet.CreateJournalRecord(MarketReference.Brokerfee, null, null, -brokerCost);
                }

                // everything looks okay, update the price of the order
                this.DB.UpdatePrice(connection, order.OrderID, newPrice, newEscrow);

                // send a OnOwnOrderChange notification
                call.Client.NotifyMultiEvent(new OnOwnOrderChanged(order.TypeID, "Modified"));
            }
            finally
            {
                this.DB.ReleaseMarketLock(connection);
            }

            return(null);
        }
Beispiel #14
0
        public PyDataType AssembleCargoContainer(PyInteger containerID, PyDataType ignored, PyDecimal ignored2,
                                                 CallInformation call)
        {
            ItemEntity item = this.ItemFactory.GetItem(containerID);

            if (item.OwnerID != call.Client.EnsureCharacterIsSelected())
            {
                throw new TheItemIsNotYoursToTake(containerID);
            }

            // ensure the item is a cargo container
            switch (item.Type.Group.ID)
            {
            case (int)Groups.CargoContainer:
            case (int)Groups.SecureCargoContainer:
            case (int)Groups.AuditLogSecureContainer:
            case (int)Groups.FreightContainer:
            case (int)Groups.Tool:
            case (int)Groups.MobileWarpDisruptor:
                break;

            default:
                throw new ItemNotContainer(containerID);
            }

            bool oldSingleton = item.Singleton;

            // update singleton
            item.Singleton = true;
            item.Persist();

            // notify the client
            call.Client.NotifyMultiEvent(OnItemChange.BuildSingletonChange(item, oldSingleton));

            return(null);
        }
Beispiel #15
0
 public PyDataType GetRecruitmentAdsByCriteria(PyInteger regionID, PyDecimal skillPoints, PyInteger typeMask,
                                               PyInteger raceMask, PyInteger isInAlliance, PyInteger minMembers, PyInteger maxMembers, CallInformation call)
 {
     return(this.DB.GetRecruitmentAds(regionID, skillPoints, typeMask, raceMask, isInAlliance, minMembers, maxMembers));
 }
Beispiel #16
0
        public PyBool InsureShip(PyInteger itemID, PyDecimal insuranceCost, PyInteger isCorpItem, CallInformation call)
        {
            int callerCharacterID = call.Client.EnsureCharacterIsSelected();

            if (this.ItemFactory.TryGetItem(itemID, out Ship item) == false)
            {
                throw new CustomError("Ships not loaded for player and hangar!");
            }

            Character character = this.ItemFactory.GetItem <Character>(callerCharacterID);

            if (isCorpItem == 1 && item.OwnerID != call.Client.CorporationID && item.OwnerID != callerCharacterID)
            {
                throw new MktNotOwner();
            }

            if (item.Singleton == false)
            {
                throw new InsureShipFailed("Only assembled ships can be insured");
            }

            if (this.DB.IsShipInsured(item.ID, out int oldOwnerID, out int numberOfInsurances) == true && (call.NamedPayload.TryGetValue("voidOld", out PyBool voidOld) == false || voidOld == false))
            {
                // throw the proper exception based on the number of insurances available
                if (numberOfInsurances > 1)
                {
                    throw new InsureShipFailedMultipleContracts();
                }

                throw new InsureShipFailedSingleContract(oldOwnerID);
            }

            using Wallet wallet = this.WalletManager.AcquireWallet(character.ID, 1000);
            {
                wallet.EnsureEnoughBalance(insuranceCost);
                wallet.CreateJournalRecord(
                    MarketReference.Insurance, this.ItemFactory.SecureCommerceCommision.ID, -item.ID, -insuranceCost, $"Insurance fee for {item.Name}"
                    );
            }

            // insurance was charged to the player, so old insurances can be void now
            this.DB.UnInsureShip(item.ID);

            double fraction = insuranceCost * 100 / item.Type.BasePrice;

            // create insurance record
            DateTime expirationTime = DateTime.UtcNow.AddDays(7 * 12);
            int      referenceID    = this.DB.InsureShip(item.ID, isCorpItem == 0 ? callerCharacterID : call.Client.CorporationID, fraction / 5, expirationTime);

            // TODO: CHECK IF THE INSURANCE SHOULD BE CHARGED TO THE CORP

            this.MailManager.SendMail(this.ItemFactory.SecureCommerceCommision.ID, callerCharacterID,
                                      "Insurance Contract Issued",
                                      "Dear valued customer, <br><br>" +
                                      "Congratulations on the insurance on your ship. A very wise choice indeed.<br>" +
                                      $"This letter is to confirm that we have issued an insurance contract for your ship, <b>{item.Name}</b> (<b>{item.Type.Name}</b>) at a level of {fraction * 100 / 30}%.<br>" +
                                      $"This contract will expire at <b>{expirationTime.ToLongDateString()} {expirationTime.ToShortTimeString()}</b>, after 12 weeks.<br><br>" +
                                      "Best,<br>" +
                                      "The Secure Commerce Commission<br>" +
                                      $"Reference ID: <b>{referenceID}</b>"
                                      );

            return(true);
        }
Beispiel #17
0
        public PyDataType RepairItems(PyList itemIDs, PyDecimal iskRepairValue, CallInformation call)
        {
            // ensure the player has enough balance to do the fixing
            Station station = this.ItemFactory.GetStaticStation(call.Client.EnsureCharacterIsInStation());

            // take the wallet lock and ensure the character has enough balance
            using Wallet wallet = this.WalletManager.AcquireWallet(call.Client.EnsureCharacterIsSelected(), 1000);
            {
                wallet.EnsureEnoughBalance(iskRepairValue);
                // build a list of items to be fixed
                List <ItemEntity> items = new List <ItemEntity>();

                double quantityLeft = iskRepairValue;

                foreach (PyInteger itemID in itemIDs.GetEnumerable <PyInteger>())
                {
                    // ensure the given item is in the list
                    if (this.mInventory.Items.TryGetValue(itemID, out ItemEntity item) == false)
                    {
                        continue;
                    }

                    // calculate how much to fix it
                    if (item is Ship)
                    {
                        quantityLeft -= Math.Min(item.Attributes[Attributes.damage] * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_SHIP), quantityLeft);
                    }
                    else
                    {
                        quantityLeft -= Math.Min(item.Attributes[Attributes.damage] * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE), quantityLeft);
                    }

                    // add the item to the list
                    items.Add(item);

                    // if there's not enough money left then break the loop and fix whatever's possible
                    if (quantityLeft <= 0.0)
                    {
                        break;
                    }
                }

                quantityLeft = iskRepairValue;

                // go through all the items again and fix them
                foreach (ItemEntity item in items)
                {
                    double repairPrice = 0.0f;

                    if (item is Ship)
                    {
                        repairPrice = item.Attributes[Attributes.damage] * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_SHIP);
                    }
                    else
                    {
                        repairPrice = item.Attributes[Attributes.damage] * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE);
                    }

                    // full item can be repaired!
                    if (repairPrice <= quantityLeft)
                    {
                        item.Attributes[Attributes.damage].Integer = 0;
                    }
                    else
                    {
                        int repairUnits = 0;

                        // calculate how much can be repaired with the quantity left
                        if (item is Ship)
                        {
                            repairUnits = (int)(quantityLeft / (item.Type.BasePrice * BASEPRICE_MULTIPLIER_SHIP));
                            repairPrice = repairUnits * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_SHIP);
                        }
                        else
                        {
                            repairUnits = (int)(quantityLeft / (item.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE));
                            repairPrice = repairUnits * (item.Type.BasePrice * BASEPRICE_MULTIPLIER_MODULE);
                        }

                        // only perform changes on the damage if there's units we can pay for repair
                        if (repairUnits > 0)
                        {
                            item.Attributes[Attributes.damage] -= repairUnits;
                        }
                    }

                    quantityLeft -= repairPrice;
                    // persist item changes
                    item.Persist();
                }

                wallet.CreateJournalRecord(MarketReference.RepairBill, station.OwnerID, null, -(iskRepairValue - quantityLeft));
            }

            return(null);
        }