воскресенье, 26 июня 2011 г.

Разработка модуля закупок, автоматизирующий процесс заказа материалов–Часть 2


               Реализация

Опишем некоторые детали реализации.

·         Данные хранимые в специальных таблицах-словарях базы данных будем загружать один раз при старте приложения:
    void Application_Start(object sender, EventArgs e)
    {
        Web.Environment.Procedures.Init();
        Web.Environment.Words.Init();
        Web.Environment.Elements.Init();
    }


·      Система будет поддерживать несколько языков, соответственно для идентификации текста будем использовать числовые идентификаторы. При получении строки будем пользоваться классом Words:
public class Words
    {
        private static Dictionary> wordsDictionary;

        public static Dictionary> WordsDictionary
        {
            get { return Words.wordsDictionary; }
        }
        
        private static bool valid;

        public static bool Valid
        {
            get { return Words.valid; }
        }

        public static void Init()
        {
            wordsDictionary = Procedures.WordsSelect();
            valid = true;
        }
        public static string GetWord(int languageId, int wordId, params object[] args)
        {
            string wordText = null;
            wordsDictionary[languageId].TryGetValue(wordId, out wordText);
            if (string.IsNullOrEmpty(wordText))
            {
                wordsDictionary[languageId].TryGetValue(NO_NAME, out wordText);
                if (string.IsNullOrEmpty(wordText))
                {
                    return "No name";
                }
                return GetParametrizedWords(wordText, args);
            }
            else
            {
                return GetParametrizedWords(wordText, args);
            }
        }
        private static string GetParametrizedWords(string wordText, params object[] args)
        {
            if (args == null)
            {
                return wordText;
            }
            StringBuilder wordTextBuild = new StringBuilder();
            wordTextBuild.AppendFormat(wordText, args);
            return wordTextBuild.ToString();
        }

        public static string GetWord(int languageId, int wordId)
        {
            return GetWord(languageId, wordId, null);
        }
}


·         При занесении в систему пользовательских данных, способ хранения описанный выше не подходит из-за возможных конфликтов в идентификаторах фраз. Разработка системы ведется на отдельной  базе – копии настоящей, идентификаторы новых фраз жестко используются в интерфейсах. Если бы пользователи могли собственноручно заносить новые фразы, это привело бы к коллизиям при переносе фраз созданные в отдельной базе. Поэтому пользовательские фразы будут храниться в XML, и обеспечивать работу с этими данными будет класс:

    public class MultilangString
    {
        public MultilangString()
        {
            foreach (Currencies currency in Enum.GetValues(typeof(Currencies)).Cast())
            {
                if (currency == Currencies.NONE)
                {
                    continue;
                }

                items.Add(currency, string.Empty);
            }
        }
        public MultilangString(string xml) : this()
        {
            StringReader reder = new StringReader(xml);
            foreach (XElement str in XElement.Load(reder).Descendants("string"))
            {
                Currencies cur = (Currencies)Enum.Parse(typeof(Currencies), str.Attribute("Language").Value);
                items[cur] = str.Attribute("Value").Value;
            }
        }

        private Dictionary items = new Dictionary();

        public string this[short curId]
        {
            get
            {
                Currencies cur = (Currencies)curId;
                return this[cur];
            }
            set
            {
                Currencies cur = (Currencies)curId;
                this[cur] = value;
            }
        }
        public string this[Currencies cur]
        {
            get
            {
                if (items.ContainsKey(cur))
                {
                    return items[cur];
                }
                return string.Empty;
            }
            set
            {
                if (items.ContainsKey(cur))
                {
                    items[cur] = value;
                }
            }
        }

        public List> ToList()
        {
            return items.ToList();
        }
        public string ToXml()
        {
            XElement strings = new XElement("Strings");
            foreach (var pair in items.Where(p => !string.IsNullOrEmpty(p.Value)))
            {
                strings.Add(
                    new XElement("string",
                        new XAttribute("Language", pair.Key),
                        new XAttribute("Value", pair.Value)));
            }
            return strings.ToString();
        }
}
Основные типы данных

1.      Компонент. Единица товара, изготавливаемая на производстве. Каждый компонент наделен уникальным десятизначным номером. Материалы, необходимые для производства компонента зависят от спецификации.
Рис 10. Структура продукта
2.      Материал. Закупаемая единица используется при производстве компонентов. Материал характеризуется ценой и классом.
Рис 11. Структура материала
3.      Спецификация. Содержит набор материалов необходимых для производства компонента. Спецификация идентифицируется уникальным номером.

Рис 12. Структура спецификации

4.      Менеджер. Выполняет роль куратора заказа.
5.  Аналоги. Содержит материалы, которые можно взаимозаменить при производстве компонента.
6.      Остатки. Материалы, хранимые на производстве.
Рис 13. Структура остатков
7.      Заказ. Описывает заказ на компоненты. Может находиться в одной из пяти стадий: прогноз, план, заказ, производство, закрыт.
Рис 14. Структура заказа.


Уровень бизнес-логики

Бизнес-объекты используемые в системе делятся на два вида.
Первые -  объекты содержащие информацию принятую из базы данных. Эти объекты часто выстроены в иерархию, для того, чтобы в разных вариантах использования, использовалась только необходимая часть данных, что уменьшает нагрузку на сервер.

    public class ComponentItemBaseInformation
    {
        protected int componentId;
        public int ComponentId
        {
            get { return componentId; }
            set { componentId = value; }
        }
        protected int componentTypeId;
        public int ComponentTypeId
        {
            get { return componentTypeId; }
            set { componentTypeId = value; }
        }
        protected string custPartNumber;
        public string CustPartNumber
        {
            get { return custPartNumber; }
            set { custPartNumber = value; }
        }

        protected string description;
        public string Description
        {
            get { return description; }
            set { description = value; }
        }
        public string DescriptionView
        {
            get
            {
                return description;
            }
        }
        private int producerId;

        public int ProducerId
        {
            get { return producerId; }
            set { producerId = value; }
        }
}
Рис 15. Отношения классов
Второй – объекты-менеджеры предоставляющие возможность управлять объектами описанными выше:
Рис 16. Классы-менеджеры


Уровень доступа к данным
Доступ к базе данных осуществляется с помощью хранимых процедур:
using (SqlDataReader rd = cmd.ExecuteReader())
{
   ComponentItemGeneral tmpComponentItem;
  while (rd.Read())
  {
       tmpComponentItem = new ComponentItem();
       tmpComponentItem.ComponentId = (Int32)rd["ComponentID"];
       tmpComponentItem.ComponentTypeId = (Int32)rd["ComponentTypeID"];
       tmpComponentItem.CustPartNumber = rd["CustPartNumber"].ToString();
       tmpComponentItem.Description = Convert.ToString(rd["Description"]);
       tmpComponentItem.DescriptionRussian = Convert.ToString(rd["DescriptionRussian"]);
       tmpComponentItem.IsActive = Convert.ToBoolean(rd["IsActive"]);
       tmpComponentItem.ManufacturerId = Convert.ToInt32(rd["ManufacturerID"]);
       tmpComponentItem.RevisionDate = Convert.ToDateTime(rd["RevisionDate"]);
       tmpComponentItem.ComponentPrice = Convert.ToDouble(rd["ComponentPrice"]);
       tmpComponentItem.CurrencyID = (Currencies) Convert.ToInt16(rd["CurrencyID"]);
       tmpComponentItem.Tags = Convert.ToString(rd["Tags"]);
       tmpComponentItem.ProducerId = Convert.ToInt32(rd["ProducerID"]);
       collection.Add(tmpComponentItem);
  }
}

Все хранимые процедуры начинаются с проверки пользователя:
 EXEC p_Logon_Check @intGroupId = @intGroupId OUT,
                             @strGuid = @strGuid,
                             @intUserId = @intUserId OUT

      IF @intUserId IS NULL
            BEGIN
                  RETURN
            END
      IF @intGroupId is null
            BEGIN
                  RETURN
       END

Сами же процедуры представляют собой обычные SQL-запросы
SELECT
            b.[ComponentID]
            ,[ComponentGroupID]
            ,b.[ComponentTypeID]
            ,[CustPartNumber]
            ,[Description]
            ,CASE @DescriptionRussianConcat
                  WHEN 1 THEN [DescriptionRussian] + ' ' + [Description]
                  ELSE [DescriptionRussian] END AS [DescriptionRussian]
            ,[PricesPerUnit]
            ,[ManufacturerID]
            ,[IsActive]
            ,[RevisionDate]
            ,[PcsPerPackage]
            ,[WeightNetPackage]
            ,[WeightBruttoPackage]
            ,[DimX]
            ,[DimY]
            ,[DimZ]
            ,[ComponentPrice]
            ,[CurrencyID]
            ,[PriceDate]
            ,[MinStock]
            ,[MarginForLoss]
            ,[Picture]
            ,b.Tags
            ,b.ProducerID
      FROM dbo.List_BOM b
      INNER JOIN Component_Prices cp ON cp.PriceID = b.PriceID
      INNER JOIN Ref_Maker m ON m.MakerID = b.ManufacturerID
      INNER JOIN List_ComponentTypes ct ON ct.ComponentTypeID = b.ComponentTypeID
      WHERE b.[ComponentID] in (select [ComponentID] FROM @TempComponents)
      ORDER BY
      CASE @SortingType
            WHEN 0 THEN CustPartNumber
            WHEN 1 THEN ct.ComponentDescription
            WHEN 2 THEN Description
            WHEN 3 THEN m.TitleName
            WHEN 5 THEN DescriptionRussian
      END,
      CASE @SortingType
            WHEN 6 THEN cp.[ComponentPrice]
            WHEN 7 THEN [CurrencyID]
      END,
      CASE @SortingType
            WHEN 4 THEN RevisionDate
END DESC


Уровень представления

Отображение данных на страницах производится с использованием модели «поставщики данных». Эта модель включает специальные объекты – DataSource, предоставляющие данные:
<asp:ObjectDataSource ID="odsAnalogs" runat="server"
OnObjectCreating="odsAnalogs_ObjectCreating"
OnSelecting="odsAnalogs_Selecting"
SelectMethod="AnalogElementsSelect"
TypeName="GS.MConvoy.Web.Tables.ComponentItems"
OnUpdated="odsAnalogs_Updated"
OnUpdating="odsAnalogs_Updating"
UpdateMethod="AnalogComponentsUpdate"
DeleteMethod="AnalogComponentDelete"
OnDeleted="odsAnalogs_Deleted"
OnDeleting="odsAnalogs_Deleting">
SelectParameters>
asp:ObjectDataSource>

Эти объекты взаимодействуют с объектами-менеджерами с бизнес-уровня, для получения информации из базы данных. Для отображения используются другие объекты. Будучи подключенными к источнику данных, они отображают данные по заданному шаблону:
      SkinID="ComponentPage"
      AllowPaging="True"
      PageSize="13" AutoGenerateColumns="False"
      DataSourceID="odsAnalogs"
      OnDataBound="grvAnalogs_DataBound"
      DataKeyNames="ComponentId"
      OnRowDeleting="grvAnalogs_RowDeleting">
      Position="TopAndBottom" />
     
            ShowDeleteButton="True" />
     
           
           
                  ID="lnkCustPartNumber"
                  Text='<%# Eval("CustPartNumber")%>'
                  NavigateUrl='<%#Eval("ComponentLink")%>'>
           
     
     
            HeaderText="Description" ReadOnly="true"
            SortExpression="Description" />
     
            HeaderText="ComponentPrice" ReadOnly="true"
            SortExpression="ComponentPrice" />
     
            HeaderText="CurrencyName" ReadOnly="true"
            SortExpression="CurrencyName" />
     
            HeaderText="ManufacturerIdView" ReadOnly="true"
            SortExpression="ManufacturerIdView" />
     
            HeaderText="RevisionDateView" ReadOnly="true"
            SortExpression="RevisionDateView" />
Columns>
asp:GridView

Выводы
В ходе работы был разработан модуль для автоматизации бизнес-процессов при работе с заказами на производственной фирме. Модуль включает в себя подсистему управления данными о продукте, подсистему управления остатками, подсистему управления заказами.
В ходе данной работы были достигнуты следующие результаты:
·         Изучены существующие решения в сфере автоматизации производства
·         Изучена платформа .Net, созданная для разработки приложений
·         Разработано техническое задание и спецификация системы
·         Разработана архитектура системы
·         Реализовано приложение, отвечающее заданным требованиям
·         Приложение протестировано. Найденные ошибки устранены. 

Комментариев нет:

Отправить комментарий