Jak zaimplemetnować logikę bizesową

0

Witam,
Ponieważ pierwszy raz zajmuje się logiką biznesową proszę o przykład w jaki sposób mógłbym zaimplementować funkcje ObliczMarze() oraz ObliczCeneSprzedazy() żeby obliczało mi poprawnie pola z bazy danych. Oczywiście mogą być różne pomysły

Z góry dziękuję i Pozdrawiam

Poniżej klasa dodająca towary z właściwościami odpowiadającymi polom w bazie danych.

using MVVMFirma.Model.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMFirma.ViewModel
{


    public class NowyTowarViewModel : JedenViewModel<TowaryWMagazynie>, IDataErrorInfo
     {
             #region Konstruktor
        public NowyTowarViewModel()
        {
            base.DisplayName = "Nowy Towar";
            item = new TowaryWMagazynie();
            
        }

        #endregion Konstruktor



        //public decimal? ObliczMarze()
        //{
        //    return
        //        (
        //        from pozycja in fakturyEntities.TowaryWMagazynie

        //        select pozycja.CenaZakupuBrutto * pozycja.Marza / 100 
        //        ).Sum();
        //}

        //public decimal? ObliczCeneSprzedazy()
        //{
        //    return
        //        (
        //        from pozycja in fakturyEntities.TowaryWMagazynie
        //        select pozycja.CenaZakupuBrutto + pozycja.Marza
        //        ).Sum();
        //}

        #region Propertis
        //Dla każdego pola edytowalnego dla widoku należy utworzyć propertisa.
        public int IdJednostkiMiary
        { 
            get
            {
                return item.IdJednostkiMiary; //Najerzdzamy na pole i sprawdzamy czy z znakiem zapytania.
            }
            set
            {
                if(item.IdJednostkiMiary != value)
                {
                    item.IdJednostkiMiary = value;
                    OnPropertyChanged(() => IdJednostkiMiary);
                }               
            }
        }
        public IQueryable<Asortyment_JM> JednostkiMiaryComboboxItems
        {
            get
            {
                return
                    (
                      from jednostkiMiary in fakturyEntities.Asortyment_JM
                      where jednostkiMiary.Active == true
                      select jednostkiMiary
                    ).ToList().AsQueryable(); //Wypełnienie comboBox-a Jednostkami Miary.
            }
        }
      
        public int IdStawkiVat
        {
            get
            {
                return item.IdStawkiVat;
            }
            set
            {
                if (item.IdStawkiVat != value)
                {
                    item.IdStawkiVat = value;
                    OnPropertyChanged(() => IdStawkiVat);
                }
            }
        }
        //Zrobimy teraz propertisa obsługującego ComboBoxa.
       public IQueryable<Asortyment_KODVAT> StawkiVATComboboxItems
        {
           get
            {
                return
                    (
                      from stawkaVAT in fakturyEntities.Asortyment_KODVAT
                      where stawkaVAT.Active == true
                      select stawkaVAT
                    ).ToList().AsQueryable();
            }
        }
 
       public string KodTowaru
       {
           get
           {
               return item.KodTowaru;
           }
           set
           {
               if (item.KodTowaru != value)
               {
                   item.KodTowaru = value;
                   OnPropertyChanged(() => KodTowaru);
               }
           }
       }
       public string NazwaTowaru
       {
           get
           {
               return item.NazwaTowaru;
           }
           set
           {
               if(item.NazwaTowaru !=value)
               {
                   item.NazwaTowaru = value;
                   OnPropertyChanged(() => NazwaTowaru);
               }
           }
       }
       public decimal Stan
       {
           get
           {
               return item.Stan;
           }
           set
           {
               if(item.Stan != value)
               {
                   item.Stan = value;
                   OnPropertyChanged(()=> Stan);
               }
           }
       }
       public decimal? CenaZakupuBrutto
       {
           get
           {
               return item.CenaZakupuBrutto;
           }
           set
           {
               if (item.CenaZakupuBrutto != value)
               {
                   item.CenaZakupuBrutto = value;
                   OnPropertyChanged(() => CenaZakupuBrutto);
               }
           }
       }
       public decimal? Marza
       {
           get
           {
               return item.Marza;
           }
           set
           {
               if (item.Marza != value)
               {
                   item.Marza = value;
                   OnPropertyChanged(() => Marza);
               }
           }
       }
       public decimal? CenaSprzedazyBrutto
       {
           get
           {
               return item.CenaSprzedazyBrutto;
           }
           set
           {
               if (item.CenaSprzedazyBrutto != value)
               {
                   item.CenaSprzedazyBrutto = value;
                   OnPropertyChanged(() => CenaSprzedazyBrutto);
               }
           }
       }



       public bool Active
       {
           get
           {
               return item.Active;
           }
           set
           {
               if(item.Active !=value)
               {
                   item.Active = value;
                   OnPropertyChanged(() => Active);
               }
           }
       }
      
        #endregion Propertis

       #region Validation
       public string Error
       {
           get
           {
               return null;
           }
       }
      

        #region Helpers
        public override void save()
        {
            fakturyEntities.TowaryWMagazynie.Add(item);    
            fakturyEntities.SaveChanges(); 
        }

      
        #endregion Helpers
    }
}

 
1

Skoro to logika biznesowa, sam o tym mówisz, to nie obliczaj jej w warstwie prezentacji - czyli nie w viewModelach, ale w "Modelach" lub serwisach które te modele przygotowują Ci.
Nie znam wzorca MVVM, ale szybki research w internetach powiedział mi, że w nim też, w VM nie powinieneś mieć logiki biznesowej, lecz w Modelu.

Tutaj np mówią, że to bardzo zły pomysł ;-)
http://stackoverflow.com/questions/16338536/mvvm-viewmodel-and-business-logic-connection

Generalnie w tego typu wzorcach (tak jak w MVC i MVP) to część nazwana Model (nie mylić z samym pustym obiektem trzymającym dane z bazy, model nie powinien być anemiczny) odpowiada za logikę biznesową.

0

Dziekuje za odpowiedz. Wiem że logika powinna być w Modelu tutaj zamiesciłem specjalnie te dwie funkcje wykomentowane żeby troche skrocić mi chodzi o sam sposób implementacji można to zrobić na dwa sposoby albo podpiąć funkcje pod buttona ObliczMarżę albo obliczanie w locie. Chciałbym żeby ktoś podał przykład implementacji.

0

W skrócie MVVM :
Model - logika biznesowa + połączenia z bazą
View - xaml - iterfejsy - widoki które się wyswietlają urzytkownikowi
ViewModel warstwa spinająca Model z View - klasy pośredniczące

0

Czy na kliknięcie czy w locie - to zależy. Zależy jak sobie to zaplanowałeś i jak będzie wygodniej dla użytkownika. Ewentualne kwestie optymalizacji pomijamy na razie, nie wyświetlasz miliona rekordów.
Jak chcesz mieć od razu - to wcześniej. Jak w międzyczasie od wyświetlenia do kliknięcia jakieś wartości w bazie mogłyby się zmienić (czynnik zewnętrzny, albo użytkownik ręcznie coś wprowadza), to po kliknięciu żeby były aktualne.

Co do konkretnej implementacji - to trzeba by zobaczyć jak wyglądają dokładnie encje, ale Twój kod (nie sprawdzałem, nie zastanawiałem się) jeśli liczy poprawnie to czemu Ci nie odpowiada?

0

Najlepszym przykładem implementacji byłby przykład który po wprowadzeniu kwoty zakupu brutto i marzy po kliknieciu oblicz lub przy zapisie obliczałby pole CenaSprzedazyBrutto

0

Ale co w tym trudnego? Rozumiem że kwota zakupu brutto, to "Za ile kupił sprzedawca", marża to jego marża (masło to jego masło), a cena CenaSprzedazyBrutto to to za ile sprzedał...

Czyli tak jak Ty to napisałeś:
CenaSprzedazyBrutto = CenaZakupuBrutto + Marza;
Zakładając że to są pola jakiegoś produktu. Ewentualnie, jeśli marża jest wyrażona nie jako stała wartość a na przykłąd procent, to odpowiednie obliczenia na przykład
CenaSprzedazyBrutto = CenaZakupuBrutto + CenaZakupuBrutto * Marza;
To już zależy jakie masz pola w klasie i co przedstawiają

0

Witam,

Napisałem logikę która oblicza marżę ale chciałbym żeby metody

 ObliczMarze()  ObliczCeneSprzedazy()

obliczające były zapytaniem Linqu.
Bardzo proszę o implementację w Linqu.
Poniżej kod:

Warstwa Model:

 
using MVVMFirma.Model.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMFirma.Model.BussinesLogic.TowaryWMagazynie
{
   public class MarzaB:DatabaseClass
    {
       #region Konstruktor
        public MarzaB(FakturyEntities fakturyEntities)
            : base(fakturyEntities)
        {

        }

        #endregion Konstruktor

        #region BussinesFunction

        public decimal? ObliczMarze(decimal? cenaZakupuBrutto, decimal? marza )
        {

            return cenaZakupuBrutto * marza / 100;
        }

    

       public decimal? ObliczCeneSprzedazy(decimal? cenaZakupuBrutto, decimal? marza )
       {
          

          return ObliczMarze( cenaZakupuBrutto, marza) + cenaZakupuBrutto;
       }

        #endregion BussinesFunction
    }
}

using MVVMFirma.Model.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMFirma.Model.BussinesLogic.TowaryWMagazynie
{
   public class DatabaseClass
    {
        #region Fields
        protected FakturyEntities fakturyEntities;
        #endregion Fields

        #region Konstruktor
        public DatabaseClass(FakturyEntities fakturyEntities)
        {
            this.fakturyEntities = fakturyEntities;
        }
        #endregion Konstruktor
    }
}

Warstwa ViewModel

using MVVMFirma.Helpers;
using MVVMFirma.Model.BussinesLogic.TowaryWMagazynie;
using MVVMFirma.Model.Entities;
using MVVMFirma.Model.Validatory;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace MVVMFirma.ViewModel
{
    public class NowyTowarViewModel : JedenViewModel<TowaryWMagazynie>
     {
    
        #region Konstruktor
        public NowyTowarViewModel()
        {
            base.DisplayName = "Nowy Towar";
            item = new TowaryWMagazynie();
            CenaZakupuBrutto = 0;
            Marza = 0;
        }

        #endregion Konstruktor

        #region Propertis
        public int IdJednostkiMiary
        { 
            get
            {
                return item.IdJednostkiMiary; //Najerzdzamy na pole i sprawdzamy czy z znakiem zapytania.
            }
            set
            {
                if(item.IdJednostkiMiary != value)
                {
                    item.IdJednostkiMiary = value;
                    OnPropertyChanged(() => IdJednostkiMiary);
                }               
            }
        }
        public IQueryable<Asortyment_JM> JednostkiMiaryComboboxItems
        {
            get
            {
                return
                    (
                      from jednostkiMiary in fakturyEntities.Asortyment_JM
                      where jednostkiMiary.Active == true
                      select jednostkiMiary
                    ).ToList().AsQueryable(); //Wypełnienie comboBox-a Jednostkami Miary.
            }
        }
         public int IdStawkiVat
        {
            get
            {
                return item.IdStawkiVat;
            }
            set
            {
                if (item.IdStawkiVat != value)
                {
                    item.IdStawkiVat = value;
                    OnPropertyChanged(() => IdStawkiVat);
                }
            }
        }
              public IQueryable<Asortyment_KODVAT> StawkiVATComboboxItems
        {
           get
            {
                return
                    (
                      from stawkaVAT in fakturyEntities.Asortyment_KODVAT
                      where stawkaVAT.Active == true
                      select stawkaVAT
                    ).ToList().AsQueryable(); //Wypełnienie comboBox-a stawkami Vat.
            }
        }
  
       public string KodTowaru
       {
           get
           {
               return item.KodTowaru;
           }
           set
           {
               if (item.KodTowaru != value)
               {
                   item.KodTowaru = value;
                   OnPropertyChanged(() => KodTowaru);
               }
           }
       }
       public string NazwaTowaru
       {
           get
           {
               return item.NazwaTowaru;
           }
           set
           {
               if(item.NazwaTowaru !=value)
               {
                   item.NazwaTowaru = value;
                   OnPropertyChanged(() => NazwaTowaru);
               }
           }
       }
       public decimal Stan
       {
           get
           {
               return item.Stan;
           }
           set
           {
               if(item.Stan != value)
               {
                   item.Stan = value;
                   OnPropertyChanged(()=> Stan);
               }
           }
       }

       public decimal? CenaZakupuBrutto
       {
           get
           {
               return item.CenaZakupuBrutto;
           }
           set
           {
               if (item.CenaZakupuBrutto != value)
               {
                   item.CenaZakupuBrutto = value;
                   OnPropertyChanged(() => CenaZakupuBrutto);
               }
           }
       }
       public decimal? Marza
       {
           get
           {
               return item.Marza;
           }
           set
           {
               if (item.Marza != value)
               {
                   item.Marza = value;
                   OnPropertyChanged(() => Marza);
               }
           }
       }
       public decimal? CenaSprzedazyBrutto
       {
           get
           {
               return item.CenaSprzedazyBrutto;
           }
           set
           {
               if (item.CenaSprzedazyBrutto != value)
               {
                   item.CenaSprzedazyBrutto = value;
                   OnPropertyChanged(() => CenaSprzedazyBrutto);
               }
           }
       }



       public bool Active
       {
           get
           {
               return item.Active;
           }
           set
           {
               if(item.Active !=value)
               {
                   item.Active = value;
                   OnPropertyChanged(() => Active);
               }
           }
       }
      
        #endregion Propertis

       #region Command
     
       private BaseCommand _ObliczMarzeCommand;
     
       public ICommand ObliczMarzeCommand
       {
           get
           {
               if (_ObliczMarzeCommand == null)
                   _ObliczMarzeCommand = new BaseCommand(() => obliczCenezMarzaClick());
               return _ObliczMarzeCommand;
           }
       }
       #endregion Command

        #region Helpers
        public override void save()
        {
            
            fakturyEntities.TowaryWMagazynie.Add(item);
            
            fakturyEntities.SaveChanges(); 
        }

        private void obliczCenezMarzaClick()
        {
           
            CenaSprzedazyBrutto = new MarzaB(fakturyEntities).ObliczCeneSprzedazy( CenaZakupuBrutto, Marza);
            
        }
      
        #endregion Helpers
    }
}


 
0

Nie musisz robić do tego nowej klasy, zwłaszcza w ten sposób, nie wydaje się to mieć sensu. Dziedziczysz po klasie, a później nawet nie korzystasz z jej pól - to bez sensu. DO tego tworzysz obiekt po to, żeby wywołać metodę... w takim wypadku powinien to być obiekt statyczny jak i metoda, jeśli to zwykły helper. ALe znów, to nie jest obiektowe podejście i najlepiej by było umieścić to w klasie danego produktu z którego te zmienne pochodzą. I to w modelu, może być encja, a nie w ViewMOdelu. I ja tam na ten moment nie widzę możliwości użycia LINQ . Na LINQ mógłbyś zamienić np

public IQueryable<Asortyment_KODVAT> StawkiVATComboboxItems

Do tego chyba często mieszasz liczbę mnogą z pojedynczą. Pomijając już fakt, że nazywasz zmienne po polsku - czego odradzam, ale rozumiem że na początku może być ciężko inaczej.
Jak Twoja zmienna(lub klasa) to kolekcja (lista, tablica, IQuerable) wielu elementów, to ma sens, żeby zmienna była w liczbie mnogiej np faktury, ale gdy jest tylko jedna, to nazywaj to faktura. Przykład:

item = new TowaryWMagazynie()

Czy ten item to lista wielu towarów w magazynie? Może powinno być items? Czy klasa to tak naprawdę TowarWMagazynie?

0

Delikatnie mówiąc czepiasz się. Akurat to projekt który realizuje w ramach studiów wiec jest ok tak samo nazewnictwo powinno być w języku angielskim ale za pierwszym razem jak się uczymy możemy ułatwić trochę sprawę. Generalnie powinno się dawać jak najdłuższe nazwy zrozumiałe dla nas (zero skracania nazw) aby np za 5 lat była możliwość szybka analiza kodu. Aplikacja jest bardzo rozbudowana wszystko jak dotąd działa ale z tą logiką biznesową poszedłem na skróty. w powyższym przykładzie bo profesjonalna logika biznesowa to C# oraz zapytania Linq np.

Sprawdzamy utarg w wybranym okresie.

   public decimal? UtargOkresTowar(int idTowaru, DateTime odDaty, DateTime doDaty)
        {
            return
                (
                from pozycja in fakturyEntities.FakturySprzedazyPozycje
                where pozycja.IdTowaru == idTowaru && pozycja.FakturySprzedazy.DataWystawienia >= odDaty && pozycja.FakturySprzedazy.DataWystawienia <= doDaty
                select pozycja.WartoscBrutto * pozycja.Ilosc
                ).Sum();
        }
 

no własnie tak logika biznesowa powinna mniej więcej wyglądać.

Teraz robię Fakturę sprzedaży i już mam problem bo tam tylko mogę zastosować zapytanie linqu z c# (trzeba się odwołać do innej tabeli gdzie jest stawka vat) a w klasie z pozycją faktury mam comboboxa z IdStawkiVat
dlatego proszę o pomoc na forum z zapytaniami Linqu.

Napisałem coś takiego ale niestety nie działa.

  public decimal? ObliczVat(decimal? ilosc, decimal? cenaNetto, int? IdstawkaVat)
        {
            return
                (
                from pozycja in fakturyEntities.FakturyZakupuPozycje
                where pozycja.Ilosc == ilosc && pozycja.CenaNetto == cenaNetto && pozycja.Asortyment_KODVAT.StawkaVat == IdstawkaVat
                select pozycja.Ilosc * pozycja.CenaNetto * pozycja.Asortyment_KODVAT.StawkaVat / 100
                ).Sum();
        }
 

Proszę o pomoc i z góry dziękuję za wszelkie sugestie.

1
przemo27ns napisał(a):

profesjonalna logika biznesowa to C# oraz zapytania Linq

Logika biznesowa, inaczej zwana modelem, to odzwierciedlenie procesów związanych z dziedziną systemu. Jest ona niezależna od źródeł danych i od interfejsu użytkownika (którego zresztą w ogóle może nie być).
Ty piszesz jakiś kod operujący bezpośrednio na źródle danych (a konkretnie kontekście EF), więc nie jest to logika biznesowa.

  public decimal? ObliczVat(decimal? ilosc, decimal? cenaNetto, int? IdstawkaVat)
        {
            return
                (
                from pozycja in fakturyEntities.FakturyZakupuPozycje
                where pozycja.Ilosc == ilosc && pozycja.CenaNetto == cenaNetto && pozycja.Asortyment_KODVAT.StawkaVat == IdstawkaVat
                select pozycja.Ilosc * pozycja.CenaNetto * pozycja.Asortyment_KODVAT.StawkaVat / 100
                ).Sum();
        }

Ten kod to jakieś zaczarowane spaghetti. Napisałeś metodę, która przyjmuje ilość, cenę i id stawki VAT, na tej podstawie wyszukujesz rekordy z bazy, a następnie mnoży te trzy wartości, z których dwie dostaje w parametrach... Kiedy i gdzie masz zamiar wołać tę metodę? Co jej przekazać? Jaki sens ma w ogóle takie wyszukiwanie, przecież to spowoduje znalezienie pozycji z różnych faktur... Równie dobrze mógłbyś sobie losować tę wartość, bo sensu biznesowego ten kod nie ma żadnego.

W dobrze zaprojektowanym kodzie obiekt faktury ma właściwość WartośćVat, która sumuje wartości właściwości WartośćVat dla obiektów pozycji faktury, które to mają swoją WartośćNetto i odwołanie do obiektu stawki VAT. I nigdzie nie ma żadnych metod ani parametrów, bo obiekty operują tylko na swoim wewnętrznym stanie.

0

powinno być coś w tym stylu?
Generalnie mam w tabeli PozycjeFakturySprzedazy pola decimal? Ilość, decimal? CeneNetto, int? IdStawkiVat oraz Pole decimal? Vat do którego chcę obliczyć wartość vat. IdStawkiVat wybierana jest z Comboboxa.

 
public decimal? ObliczVat()
        {
            return
                (
                from pozycja in fakturyEntities.FakturyZakupuPozycje
                select new
                { 
                    pozycja.Ilosc,
                    pozycja.CenaNetto,
                    pozycja.Asortyment_KODVAT.StawkaVat,
                    Vat = pozycja.Ilosc * pozycja.CenaNetto * pozycja.Asortyment_KODVAT.StawkaVat / 100
                }
                );
        }

1 użytkowników online, w tym zalogowanych: 0, gości: 1